Как я могу использовать переменные в useEffect, не имея их в зависимости?

#reactjs #react-hooks #use-effect

#reactjs #реагирующие хуки #использование-эффект

Вопрос:

У меня есть список объектов и переменная размера.

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

Здесь React жалуется, что ObjectList не находится в зависимости, потому что, возможно, я хочу, чтобы useEffect зависел от изменений ObjectList. Однако я этого не делаю. Объекты могут иногда добавляться в список, но в этом случае я устанавливаю размер, в котором объект будет добавлен в список. Я не хочу перебирать каждый объект при добавлении нового объекта в список (список объектов изменен), только при изменении размера.

Как я могу перебирать список объектов при каждом изменении размера, но не при изменении списка объектов?

Ответ №1:

Кажется, есть два действия, влияющие на objectList : при изменении размера и при добавлении нового объекта.Вам нужно разделить эти два.

Вы можете добиться этого с помощью useReducer управления различными действиями.

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

Комментарии:

1. Хм, если бы у меня была функция onSizeChanged, я все равно мог бы перебирать список объектов даже без использования редуктора, не так ли? Но это не сработало бы, если бы я хотел, чтобы это было в функции useEffect.

2. Я только что видел ваше недавнее обновление о props.width , etc. Обновлено. В любом случае, это анти-шаблон, в котором вы используете props.width , etc в качестве начального состояния, если родительский и дочерний компоненты также обновляют эти реквизиты.

3. Хорошо, это было бы возможно, однако есть ли альтернатива? Я не использую useReducer в своем приложении. Потребовалось бы немало усилий по рефакторингу, чтобы изменить все приложение на useReducer. Я извлек пример более абстрактным, потому что я не могу опубликовать здесь весь код приложения.

4. Ну, единственное отличие заключается в том, как обновить objectList , который отправляет действие, а не через setObjectList , и доступ objectList к нему — это просто добавление state like state.objectList . Я не думаю, что это слишком большой рефакторинг, учитывая только два действия, влияющие objectList . Если это так, я чувствую что-то еще.

5. Я понимаю, что useReducer, вероятно, является лучшей практикой здесь, и я всегда хотел провести рефакторинг для useReducer, однако я хотел быстро добавить эту функцию, приложение нуждается в некоторой доработке, поскольку я пишу его во время обучения. Это работает так, как сейчас, и требуется рефакторинг, но время сейчас ограничено. Вот почему я хотел посмотреть, могу ли я просто использовать useEffect здесь. Теперь у меня есть обходной путь: я получаю список объектов локально в a const objects , но вернусь к вашему ответу, когда у меня будет время для очистки и рефакторинга всего. Следовательно, я проверяю ответ как принятый правильным.