在 React@18 中的类似 Signals 的功能

信號是指

Preact 新增了名为 Signals 的新功能。

介绍信号 – PREACT
预警信号 – PREACT

以下是一种可能的方式,可以更轻松地解决State的桶接力问题,相比于Context等。

import { signal } from "@preact/signals"

const count = signal(0)

const Counter = () => {
  const increment = () => {
    count.value++
  }

  return (
    <div>
      <p>Count: {count.value}</p>
      <button onClick={increment}>click me</button>
    </div>
  )
}

「Signals」在SolidJS中也存在,可以说是最近的趋势。
也许有些人会因为「Signals」而想从React转到Preact或SolidJS。

即使在 React@18 中,模拟信号也可以很容易地实现。

然而,使用 useSyncExternalStore 这个 Hook,我们可以轻松地在 React@18 中实现一个类似于 Signals 的功能,因此,尝试使用它来观察 React 的内部运作方式,可能也是一个不错的选择。

以下是示例代码。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
  </head>
  <body>
    <div id="app"></div>

    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
  
    <script>
////////////////////////////////////////////////////

const gV = {}

const signal = iniValue => {
  const key = Symbol()
  gV[key] = {value: iniValue, fList: new Set()}
  return {
    get value() {
      return gV[key].value
    },
    set value(newValue) {
      if (gV[key].value === newValue) return;
      gV[key].value = newValue
      gV[key].fList.forEach(f => f())
    },
    _subscribe: notify => gV[key].fList.add(notify), 
    _unsubscribe: notify => gV[key].fList.delete(notify),
    _remove: () => delete gV[key],
  }
}

const useSignal = (...signals) => signals.map(item => React.useSyncExternalStore(
  // subscribe
  notify => {
    item._subscribe(notify)
    return () => {
      // unsubscribe
      item._unsubscribe(notify)
    }
  },

  // getSnapshot
  () => item.value,
))


const count1 = signal(0)
const count2 = signal(0)

const Button1 = props => {
  useSignal(count1)
  return React.createElement('button', {
    onClick: e => count1.value++,
  }, count1.value)
}

const Button2 = props => {
  useSignal(count2)
  return React.createElement('button', {
    onClick: e => count2.value++,
  }, count2.value)
}

const App = props => {
  return React.createElement(React.Fragment, {}, [
    React.createElement(Button1),
    React.createElement(Button1),
    React.createElement(Button1),
    React.createElement(Button2),
    React.createElement(Button2),
    React.createElement(Button2),
  ])
}

const root = ReactDOM.createRoot(document.getElementById("app"))
root.render(React.createElement(App))
    
////////////////////////////////////////////////////
    </script>
  
  </body>

</html>

这里就到现场就完了。

非常感谢您一直陪伴我写下这样拙劣的文章,直到最后。

广告
将在 10 秒后关闭
bannerAds