reactjsreact-hooksuse-effect

How can I use variables in useEffect without having them in a dependency?


I have an objectList and a size variable.

const [objectList, setObjectList] = useState([]); //will be filled elsewhere
const [size, setSize] = useState([props.width, props.height]); //might change on user input

useEffect(() => {
    //When size changes iterate over objectList
    objectList.forEach(object => {
        object.set({
            width: size.width,
            height: size.height
        );
    });
}, [size]);

Here React complains, that objectList is not in dependency, because it might be, that I want to depend the useEffect on objectList changes. However I don't. Objects can be added to the list occasionally but in this case I am setting size where the object will be added to the list. I do not want to iterate over every object when I add a new object to the list (objectList changed), only when the size changed.

How can I iterate over the objectList on each size change but not on objectList change?


Solution

  • There seems to be two actions affecting the objectList: on size change and when new object is added. You need to separate these two.

    You can achieve this via useReducer to manage the different actions.

    const initialState = {
      objectList: []
    }
    
    function reducer(state, action) {
      switch (action.type) {
        case 'ADD_OBJECT': /* TODO */ return;
        case 'UPDATE_SIZE': {
           const { size } = action.payload;
           return state.objectList.map((o) => ({
             ...o,
             width: size.width,
             height: size.height
           }))
        }
        default: /* TODO */;
      }
    }
    
    function App(props) {
      const [state, dispatch] = useReducer(reducer, initialState);
    
      useEffect(() => {
        dispatch({
          type: 'UPDATE_SIZE',
          payload: {
            size: {
              width: props.width,
              height: props.height
            }
          }
        });
      }, [props.size]);
    }