【问题标题】:React useState array update problem via context通过上下文反应 useState 数组更新问题
【发布时间】:2020-06-04 23:39:52
【问题描述】:

我有一个数组,通过 useState 钩子,我尝试通过我通过上下文提供的函数将元素添加到该数组的末尾。然而,数组的长度永远不会超过 2,元素 [0, n] 其中 n 是我在第一个元素之后推送的最后一个元素。

我已经简化了一点,并没有测试过这么简单的代码,不过它并不复杂。

MyContext.tsx

interface IElement = {
  title: string;
  component: FunctionComponent<any>;
  props: any;
}

interface IMyContext = {
  history: IElement[];
  add: <T>(title: string, component: FunctionComponent<T>, props: T) => void
}

export const MyContext = React.createContext<IMyContext>({} as IMyContext)

export const MyContextProvider = (props) => {
  const [elements, setElements] = useState<IElement[]>([]);

  const add = <T extends any>(title: string, component: FunctionComponent<T>, props: T) => {
    setElements(elements.concat({title, component, props}));
  }

  return (
    <MyContext.Provider values={{elements, add}}>
      {props.children}
    </MyContext.Provider>
  );
}

在其他元素中,我使用此上下文添加元素并显示当前元素列表,但无论添加多少,我都只会得到 2 个。

我通过 onClick 从各种元素中添加,并通过使用添加的组件的侧边栏显示。

SomeElement.tsx

const SomeElement = () => {
  const { add } = useContext(MyContext);

  return (
    <Button onClick=(() => {add('test', MyDisplay, {id: 42})})>Add</Button>
  );
};

DisplayAll.tsx

const DisplayAll = () => {
  const { elements } = useContext(MyContext);

  return (
    <>
      {elements.map((element) => React.createElement(element.component, element.props))}
    </>
  );
};

【问题讨论】:

  • 你在哪里只看到这两个元素?是通过history吗? history 设置为什么?
  • Array.concat 连接两个数组,你正在连接一个对象。应该是elements.concat([{title, component, props}])
  • 不真实,@Ibraheem。要连接的项目可以直接传入;您不必传入数组。观察[].concat('woo') -&gt; ['woo']
  • @Jacob 谢谢你!
  • history 是否应该与elements 相同,或者两者之间是否存在某些层?我很困惑,因为您传递的是&lt;MyContext.Provider values={{history, add}}&gt;(也应该只是value)。只是想确保您正确地实例化您的提供程序。

标签: javascript reactjs react-hooks react-context


【解决方案1】:

听起来您的问题是在一次渲染中多次调用add。您可以通过使用 setState 的回调版本来避免在当前接受的答案中添加 ref:

const add = <T extends any>(title: string, component: FunctionComponent<T>, props: T) => {
  setElements(elements => elements.concat({title, component, props}));
};

使用回调版本,您将始终确保引用 当前 状态,而不是闭包中可能陈旧的值。

【讨论】:

  • 这很好用。我还设法清理了删除和重新排列方法的代码,并进行了一般性的重构,使代码库中的所有内容看起来都更好。我没有意识到 setter 有回调版本,我认为文档并没有很好地说明这一点。所以谢谢你。
  • @Jacob 你有没有提到它在哪里声明回调参数是 current 状态而不是 previous 状态?
【解决方案2】:

如果您在同一个渲染中多次调用 add,以下内容可能会有所帮助:

interface IElement = {
  title: string;
  component: FunctionComponent<any>;
  props: any;
}

interface IMyContext = {
  history: IElement[];
  add: <T>(title: string, component: FunctionComponent<T>, props: T) => void
}

export const MyContext = React.createContext<IMyContext>({} as IMyContext)

export const MyContextProvider = (props) => {
  const [elements, setElements] = useState<IElement[]>([]);
  const currentElements = useRef(elements);

  const add = <T extends any>(title: string, component: FunctionComponent<T>, props: T) => {
    currentElements.current = [...currentElements.current, {title, component, props}];
    setElements(currentElements.current);
  }

  return (
    <MyContext.Provider values={{history, add}}>
      {props.children}
    </MyContext.Provider>
  );
}

【讨论】:

  • 在我的代码中,我还有一些方法来排列和删除元素,然后他们需要对 currentElements 执行相同的操作以保持两个数组同步。 TBH:我很困惑,为什么我现在有两个阵列?我是否需要两者兼而有之才能完成这项工作?特别是考虑移动和删除元素的方法?
猜你喜欢
  • 2020-02-02
  • 2020-09-27
  • 2022-12-06
  • 1970-01-01
  • 2021-08-18
  • 2021-04-19
  • 2021-07-22
  • 2020-07-03
  • 2021-04-15
相关资源
最近更新 更多