首页 > 解决方案 > React useRef 或模块范围以存储与 UI 无关的状态

问题描述

假设currentSelected在 React 功能组件中调用了一个独立于 UI 的状态。它存储当前选择的项目,并将在某个时间使用。

有两种方法可以将状态useRef hook或模块范围存储在组件之外。

useRef 钩子:

function Example() {
  const currentSelected = useRef()

  useEffect(() => {
    // access currentSelected state
  })

  function handleClick(item) {
    currentSelected.current = item
  }

  return (
      <ul>
        {items.map(item => <li onClick={() => handleClick(item)}>item.name</li>)}
      </ul>
  )
}

模块范围:

let currentSelected = null
function Example() {

  useEffect(() => {
    // access currentSelected state
  })

  function handleClick(item) {
    currentSelected = item
  }

  return (
      <ul>
        {items.map(item => <li onClick={() => handleClick(item)}>item.name</li>)}
      </ul>
  )
}

哪种方法更适合存储与 UI 无关的状态currentSelected

以及存储状态的应用场景useRef和模块范围是什么?

========= 更新===========

UI 独立意味着您不想在更新状态后触发重新渲染。相比之下,UI 相关的状态就是这样做的。

标签: javascriptreactjsecmascript-6react-hooks

解决方案


useRef模块范围变量之间的区别

为了完整起见,我也会加入useState

  • useState:与组件实例相关联的不可变数据,并通过setter函数在更改时触发渲染。
  • useRef:与组件实例相关联的可变数据,但不会在更改时触发任何渲染。
  • 模块范围变量:与模块相关联的可变数据,由于它完全在 React 之外,因此也不会在更改时触发任何渲染。

用例useRef

如果您不止一次地安装一个组件,例如在两个页面上使用它,useRef将确保每个组件实例都有自己的可变值。

// Here, the Example component could be used in multiple places
// and each instance would successfully keep its own state while
// not triggering renders on changes.
function Example() {
  const currentSelected = useRef()

  useEffect(() => { /* access currentSelected state */ })

  return (
      <ul>
        {items.map(item => (
          <li onClick={() => { currentSelected.current = item }}>{item.name}</li>
        ))}
      </ul>
  )
}

模块范围变量的用例

如果您正在寻找类似单例的模式或类似静态的变量,例如对于某种应用程序范围的共享数据,那么模块范围变量将使这成为可能,就像在任何 vanilla JS 模块中一样。

// Here, the count value will be initialized once and then shared between every
// instances across the app, ever.

let count = 0;
function Example() {
  // Won't trigger a render, so some Example instance could still show the old value.
  count += 1;

  return <span>Combined renders of all the Example components: {count}</span>;
}

请注意,它不会在count更改时触发渲染,因此您不应该那样使用它。


注意事项

如果在组件也只挂载一次的地方只使用一次,这两种模式的行为似乎相似,但最终,触发重新挂载只是时间问题,然后您将面临一个奇怪的错误。

在对包含模块范围变量的模块进行单元测试时,您也可能会遇到问题,因为它可能无法在测试用例之间正确重置。一个快速的解决方法是只导出变量并让测试用例更改其值,但请注意不要在其他任何地方更改它。尽管这应该根据具体情况进行评估。


推荐阅读