通过上下文反应useState数组更新问题

时间:2020-06-04 23:39:52

标签: javascript reactjs react-hooks react-context

我有一个数组,它通过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))}
    </>
  );
};

2 个答案:

答案 0 :(得分:1)

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

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

使用回调版本,您将始终确保引用了 current 状态,而不是在闭包中使用过时的值。

答案 1 :(得分:0)

如果您要在同一渲染器中多次调用添加,则以下内容可能会有所帮助:

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>
  );
}