Search code examples
reactjsreact-hooksreact-context

Update a "UseContext array" fail to re-render/generate a new component


Using context to store an array and try to array.map to generate a list of component, but the UI did not re-render to show all the component.

Program background:

I have a context which contain a array and the UI will have a "Add" button.

My Goal:

  1. when user press "Add"
  2. array in context will be pushed a new value in it
  3. UI will be rerender and generate a new component

My problem:

I have try to use useState to store the array and that is completely OK since, once I update the list using setArray, that will triggle re-render and display a new component.

However, when I try to use useContext, the page will not be rerendered.

  1. Should I still use useState to perform my goal? (then I have to manage 2 varible which are the context and state, I think that is a dummy way to work on it.)
  2. is there any other method to achievel my goal?

Here is the sandbox demo: https://codesandbox.io/s/falling-field-ur748?file=/src/App2.js


Solution

  • The component subscribing the context will re-render only when the value provided by the context changes.

    Pushing the value to the list will not work here, because the reference of the list Array will not change when you push . So you should use the combination of state and context in this case .

    Maintain the list in the state and pass it along with the state updater setList as the value to the Provider.

    const [ list , setList ] = React.useState([121, 123])
    
      return (
        <UserContext.Provider value={[list, setList]}>
          <App2 />
        </UserContext.Provider>
      );
    

    Now update the state from the component which subscribes the context .

    export default function App2() {
      const [list, setList] = useContext(UserContext);
      const buttonOnClick = () => {
          setList(prevList => [...prevList, 321])
      };
      return (
        <>
          <button onClick={buttonOnClick}>Add</button>
          {list.map((item) => (
            <p>{item}</p>
          ))}
        </>
      );
    }