React useReducer 在严格模式下复制结果

问题描述 投票:0回答:2

严格模式触发我的减速器功能两次,这是预期的。

我没想到的是返回的状态中有重复的元素。

我意识到我没有正确使用useReducer,但我不知道如何正确重写我的reducer函数。

下面的示例应在初始渲染时显示 1 个时间戳,然后每次单击

Add Element
时,应添加 1 个额外时间戳。 发生的情况(使用严格模式)是每次单击按钮都会添加 2 个额外的时间戳。

import { useReducer } from 'react';

const reducer = (state, action) => {
  if (action.type === 'addElement') {
    let newState = { ...state };
    newState.elements.push({
        id: (new Date()).toString()
    })
    return newState;
  }
}

export default function Test() {
  const [state, dispatch] = useReducer(reducer, {
    elements: [
        { id: (new Date()).toString() }
    ]
  });

  function addElement() {
    dispatch({
        type: 'addElement'
    });
  }

  return (
    <>
        <h2>Elements</h2>
        {[...Array(state.elements.length)].map((e, i) => <div key={i}>
            <span>{state.elements[i].id}</span><br />
        </div>
        )}
        <button type='button' onClick={addElement}>Add Element</button>
    </>
  );
}
reactjs react-hooks
2个回答
0
投票
  • 您正在使用
    .push()
    方法直接改变原始状态数组,即使您分散了状态的其余部分。
  • 不要使用
    .push()
    ,而是将现有数组展开到新数组中并使用
    [...state.elements, newElement]
    添加新元素。
  • 相应地更新您的减速器函数,这里是如何执行相同操作的代码片段。
const reducer = (state, action) => {
  if (action.type === 'addElement') {
    return {
      ...state,
      elements: [...state.elements, { id: new Date().toString() }]
    };
  }
  return state;
};

0
投票

来自“严格模式触发了我的减速器功能两次,这是预期的。”从代码中可以明显看出,您将两次推入数组,这是无意的副作用。

React

StrictMode
组件重新运行某些生命周期方法并挂钩两次,以帮助您检测代码中的逻辑问题/错误。

请参阅旧版文档中的检测意外副作用,它在解释更详细的细节方面做得更好。我已经强调了相关点。

严格模式无法自动为你检测副作用,但它 可以通过使它们更具确定性来帮助您发现它们。 这是通过有意双重调用以下函数来完成的:

  • 类组件
    constructor
    render
    shouldComponentUpdate
    方法
  • 类组件静态
    getDerivedStateFromProps
    方法
  • 功能组件体
  • 状态更新器函数(
    setState
    的第一个参数)
  • 传递给
    useState
    useMemo
    useReducer
  • 的函数

reducer 函数执行了两次,问题是您直接改变当前状态对象。在 React 中,我们从不直接改变状态和属性。您应该更新代码以应用不可变更新模式,即浅复制正在更新的所有状态和嵌套状态。 Array.push 直接推入源数组并对其进行变异。

您可以使用 Spread 语法来复制数组,或使用 Array.concat 追加并返回 new 数组引用。

示例:

const reducer = (state, action) => {
  switch(action.type) {
    case 'addElement':
      return {
        ...state,            // <-- shallow copy state,
        elements: [          // <-- new array reference
          ...state.elements, // <-- shallow copy array
          {                  // <-- append new data
            id: (new Date()).toString()
          }
        ],
      };

    default:
      return state;
  }
}
const reducer = (state, action) => {
  switch(action.type) {
    case 'addElement':
      return {
        ...state,                         // <-- shallow copy state,
        elements: state.elements.concat({ // <-- append new data, return new array
          id: (new Date()).toString()
        }),
      };

    default:
      return state;
  }
}

另外,仅供参考,一般不建议使用日期作为 GUID。最好使用为此设计的库。 Nano ID 是一个很棒的库,可用于快速生成足够唯一的全局 ID 值。

© www.soinside.com 2019 - 2024. All rights reserved.