Как прочитать и изменить состояние в использованииEffect IAW 'исчерпывающее-deps' правило lint - PullRequest
1 голос
/ 10 февраля 2020

У меня есть карта с наложением. У меня есть эффект, который, когда базовые данные изменяются, удаляет старое наложение и повторно dr aws новый.

Заметьте, я использую "react-hooks/exhaustive-deps", и это кажется из все, что я прочитал , что просто удаление зависимости overlay не является правильным ответом (но он работает).

Следует отметить, что данные - это пропущенная опора.

useEffect(() => {
    // remove the overlay if it is there,
    // I should have overlay as a dependency per exhaustive-reps react eslint rule
    if (overlay) map.removeOverlays(overlay);    

    // Generate the new overlay based on the new data (useCallback function)
    const newOverlay = generateNewOverlay(data)

    // Store the new overlay so I can remove it later if the data changes
    // Doesn't need to be done right away though, just before next render
    setOverlay(newOverlay);

    // Show the new overlay on my map
    map.addOverlays(newOverlay);
  }, [map, overlay, data, generateNewOverlay]);

Это, конечно, будет бесконечно l oop, потому что я устанавливаю оверлей и делаю его зависимым. Мне также не нравится сразу использовать setState в эффекте, поскольку он вызывает другой рендеринг. Что мне не хватает? Как мне добиться этой логики c?

Я прочитал около 5 вопросов с одинаковыми названиями, но они не ответили на мой вопрос.

Аналогичный вопрос , но не спрашивая, соблюдайте правило lps для исчерпывающих повторений, что для них не имеет значения, потому что они не читают состояние перед его изменением.


Редактировать:

У меня все еще есть Проблема, где useState и редуктор должны быть чистыми. Цель зависимости, с которой у меня возникла проблема (overlay), заключается в том, что я должен проверить, существует ли оверлей. Если это произойдет, мне нужно сделать не чистую вещь, а именно удалить это наложение с карты перед установкой моего нового наложения. Эта строка кода делает ее не чистой:

if (overlay) map.removeOverlays(overlay); 

Без этой строки мне бы никогда не понадобилось overlay в моем списке зависимостей, и вся эта вещь не была бы фактором. Как вы видите, принятый ответ не является чистым из-за этой строки.

Ответ useReducer имеет эту строку вне редуктора, поэтому overlay должна быть зависимостью, или ему нужно go в редукторе. Первый вариант - это то, что я пытаюсь избежать с помощью "react-hooks/exhaustive-deps", а второй ответ не является чистым.

Цените любую помощь.


Окончательное редактирование:

Я нашел лучший способ подойти к этому - правильно использовать очистку в useEffect. Мне требовалось указать состояние, чтобы я мог удалить вещи с карты позже.

useEffect(() => {
    // remove the overlay if it is there,
    // I originally needed this to store the overlay so I could remove it later, but if I use cleanup properly this isn't needed
    // if (overlay) map.removeOverlays(overlay);    

    // Generate the new overlay based on the new data (useCallback function)
    const newOverlay = generateNewOverlay(data)

    // Store the new overlay so I can remove it later if the data changes
    // Doesn't need to be done right away though, just before next render (This is what a cleanup is!)
    // setOverlay(newOverlay);

    // Show the new overlay on my map
    map.addOverlays(newOverlay);

    // New code below
    return () => {map.removeOverlays(newOverlay)};
  }, [map, data, generateNewOverlay]);

Ответы [ 4 ]

1 голос
/ 21 февраля 2020

Я думаю, что проблема в том, что ваш подход к применению этого правила для линтера слишком пассивен. Вы запускаете весь код в обратном вызове useEffect каждый раз, когда он запускается, но вы действительно хотите выполнять это только при изменении data.

Таким образом, вы можете отразить это в своем коде:

const [shouldUpdateOverlay, setShouldUpdateOverlay] = useState(false);

useEffect(() => {
    if (shouldUpdateOverlay) {
        // remove the overlay if it is there,
        // I should have overlay as a dependency per exhaustive-reps react eslint rule
        if (overlay) map.removeOverlays(overlay);    

        // Generate the new overlay based on the new data (useCallback function)
        const newOverlay = generateNewOverlay(data);

        // Show the new overlay on my map
        map.addOverlays(newOverlay);

        // Store the new overlay so I can remove it later if the data changes
        // Doesn't need to be done right away though, just before next render
        setOverlay(newOverlay);
        setShouldUpdateOverlay(false);
    }
}, [map, overlay, shouldUpdateOverlay data, generateNewOverlay]);

// use from event handlers, etc.
const updateData = (newData) => {
    setData(newData);
    setShouldUpdateOverlay(true);
};

На самом деле, это чрезмерный подход к достижению того же, чего вы могли бы достичь, имея [data] в качестве единственной зависимости в useEffect, так как это действительно единственная зависимость, которая там имеет значение. Однако я считаю, что вышесказанное должно позволить вам достичь того, что вы пытаетесь сделать, не нарушая правила линтера.

1 голос
/ 10 февраля 2020

Я предлагаю просто удалить его и добавить правило игнорирования с комментарием. Это то, что моя команда делает, и это то, что предлагает Gaearon здесь для конкретных c вариантов использования.

Примечание: мне нравится решение @ DamienFlury, если это работает (и я подозреваю, что это так).

useEffect(() => {
  // remove the overlay if it is there,
  if (overlay) map.removeOverlays(overlay);    

  // Generate the new overlay based on the new data (useCallback function)
  const newOverlay = generateNewOverlay(data)

  // Store the new overlay so I can remove it later if the data changes
  // Doesn't need to be done right away though, just before next render
  setOverlay(newOverlay);

  // Show the new overlay on my map
  map.addOverlays(newOverlay);
  // value of overlay changing should not trigger effect
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, [map, data, generateNewOverlay]);

Это подавит предупреждение, но также замаскирует любые будущие изменения зависимостей, если API эффекта изменится, так что вы в основном включены ваш собственный, чтобы гарантировать, что эффект все еще срабатывает, когда вы ожидаете.

Изменится ли overlay вне этого эффекта? IE ваш компонент когда-либо вызывает setOverlay вне эффекта?

1 голос
/ 10 февраля 2020

Чтобы добавить решение @ DamienFlury, другой метод, который можно использовать для удаления постоянно меняющейся зависимости из массива зависимостей, - это использование хука useReducer. Это позволяет вам отделить ваши переменные состояния от ваших эффектов

function reducer(state, action){
  if(action.type === 'UPDATE_OVERLAY'){
    return {
     ...state,
     overlay: action.newOverlay,  //update overlay on your state object
    }
  } else {
    return state;
  }
}

function YourComponent(yourProps){
  const [ state, dispatch ] = useReducer(reducer, initialState);

  // Other functions

  useEffect(() => {
    // remove the overlay if it is there,
    if (overlay) map.removeOverlays(overlay);    

    // Generate the new overlay based on the new data (useCallback function)
    const newOverlay = generateNewOverlay(data)

    // this also allows you to decouple your state changes from your effects!
    dispatch({ 
      type: 'UPDATE_OVERLAY', 
      newOverlay,
    });

    // Show the new overlay on my map
    map.addOverlays(newOverlay);

  }, [dispatch, map, data, generateNewOverlay]);

}

Обратите внимание, что я добавил диспетчеризацию в массив зависимостей просто как хорошая практика, Вы никогда не должны l ie о ваших зависимостях! . React гарантирует, что функция dispatch будет постоянной в течение всего времени жизни компонента.

1 голос
/ 10 февраля 2020

Вы можете использовать функцию внутри setOverlay:

useEffect(() => {
  setOverlay(prev => {
    // remove the overlay if it is there,
    // I should have overlay as a dependency per exhaustive-reps react eslint rule
    if (prev) map.removeOverlays(prev);    

    // Generate the new overlay based on the new data (useCallback function)
    const newOverlay = generateNewOverlay(data)

    // Show the new overlay on my map
    map.addOverlays(newOverlay);
    return newOverlay;
  });

}, [map, data, generateNewOverlay]);

В параметре prev вы получите текущее состояние overlay. Таким образом, вам не нужно добавлять переменную состояния overlay в массив зависимостей. Следовательно, эффект не сработает при обновлении overlay.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...