【问题标题】:React useRef or module scope to store UI-independent stateReact useRef 或模块范围以存储与 UI 无关的状态
【发布时间】:2020-01-04 09:09:35
【问题描述】:

假设在 React 功能组件中有一个名为 currentSelected 的独立于 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>
  )
}

哪种方法更适合存储像currentSelected这样的独立于UI的状态?

useRef和模块作用域在存储状态方面的应用场景是什么?

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

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

【问题讨论】:

    标签: javascript reactjs ecmascript-6 react-hooks


    【解决方案1】:

    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 更改时它不会触发渲染,因此您不应该那样使用它。


    注意事项

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

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

    【讨论】:

      【解决方案2】:

      第一个

      我自己选择第一个。因为它作为单独的内部函数工作。你可以使用多个 example 组件作为同一个类/函数。

      function check(a){
       let one = 'overwrite'+a;
       console.log(one)
      }
      
      check(1);
      check(2);//as individual inside only

      第二个

      在每个示例组件执行时覆盖 currentSelected 变量

      let one = null
      //second one
      function check() {
        one = 'overwrite';
        console.log(one)
      }
      
      console.log('Before =' + one)
      check();
      console.log('After =' + one);

      【讨论】:

        【解决方案3】:

        为什么不使用 useState 挂钩。它非常适合这种情况。

        const [currentItem, setCurrentItem] = useState();
        ...
        {items.map(item => <li onClick={() => setCurrentItem(item)}>item.name</li>)}
        

        您的示例的另一个问题是,当您更改 handleClick 中的 ref current 属性时,它不会触发渲染,因此您的函数中的 useEffect 将不会运行,因此您无法访问 ref。

        【讨论】:

          【解决方案4】:

          您的“currentItem”可能与 UI 无关,但它会依赖于组件。对?如果 currentItem 与此组件相关,则应使用 React.useState 挂钩。这正是状态的用途。

          在 OOP 编程中应该避免使用全局变量(不是常量)。例如,编写良好的 OOP 代码中只有一个全局变量,即 theApp。

          【讨论】:

          • UI 独立状态不会触发重新渲染。并且模块范围不是 global
          猜你喜欢
          • 2021-04-23
          • 2022-10-07
          • 2018-01-21
          • 2021-11-10
          • 2019-07-07
          • 2013-09-30
          • 2015-08-06
          • 1970-01-01
          • 2022-08-18
          相关资源
          最近更新 更多