Search code examples
reactjstypescriptimmutability

How to add item to immutable array in React setState in Typescript?


I want to use an immutable array to handle the state. And add an item to this list.

interface MainState {
  list: readonly []; // define immutable array
}

const Component: React.FC = () => {
  // by default use the immutable array with type
  const [classes, setClasses] = useState({ list: [] } as MainState);

  useEffect(() => {
    if (data) {
      data.forEach((thing) => {
        setClasses(prevState => {
          //
          // Call signature return types '{ list: never[]; }' and 'MainState' are incompatible.
          // The types of 'list.length' are incompatible between these types.
          // Type 'number' is not assignable to type '0'.  TS2345
          //
          const list = prevState.list.concat(thing);
          return { list };
        });
      });
    }
  });
// ...

}

I though using concat would work, since it returns a new array.


Solution

  • From the doc you provided:

    The concat() method is used to merge two or more arrays

    So you need to pass array, not element to this function, I suppose this will work:

    const list = prevState.list.concat([thing]);
    

    Also I see your code is iterating through new array and pass elements one by one, why don't you concat the whole array?

        if (data) {
            setClasses(prevState => {
              const list = prevState.list.concat(data);
              // or list = [...prevState.list, ...data];
              return { list };
            });
        }
    

    Also I think you missed type of array in you MainState, at least update it with any type, here is full example for number type:

    interface MainState {
      list: readonly number[];
    }
    
    const data = [1, 2, 3]
    
    const Component: React.FC = () => {
    
      const [classes, setClasses] = useState({ list: [] } as MainState);
    
      useEffect(() => {
        if (data) {
          setClasses(prevState => ({ list: [...prevState.list, ...data]}));
        }
      });
    
      return <div>{classes}</div>
    }