#javascript #reactjs #react-hooks #callback #use-effect
Вопрос:
Я использую компонент hook и несколько переменных состояния. Я читал об использовании useEffect()
с параметрами для получения своего рода обратного вызова после обновления состояния. Пример:
export const hookComponent = () =gt; { const [var, setVar] = useState(null); useEffect(() =gt; { //do things }, [var]) }
В этом примере useEffect()
будет выполняться при каждом setVar()
вызове. В моем случае я не хочу выполнять useEffect()
каждый раз, а только в определенных случаях. Я хотел бы предоставить setVar()
некоторую информацию, которую я могу использовать в useEffect()
подобных setVar(newValue, true)
случаях .
Примечание: Я не хочу хранить эту информацию в var.
Есть ли способ сделать это?
Комментарии:
1. в вашем
useEffect
просто проверьте, еслиvar === desiredValue
тогда//do things
2. В моем случае использования речь идет не только о значении, но и о действиях пользователя. setState не возвращает обещание, которого я мог бы дождаться в соответствующих экземплярах после набора. Или есть способ дождаться завершения состояния набора?
3. как, по-вашему, было предпринято это действие ? эта информация должна быть где-то собрана.
4. У меня есть несколько случаев, когда вызывается setState, но я хочу, чтобы это действие применялось только к некоторым из них. Соответствующие получат эту информацию. Например, в качестве дополнительного параметра
5. Похоже, что лучший способ подойти к этому-иметь другое состояние
newVar
, которое обновляется там, где вы хотели бы, чтобы выuseEffect
запускали, и зависимости от этого былиnewVar
бы, а не вашимиvar
.
Ответ №1:
Как сказал Низар, простая условная проверка на «var» в действии
Если дорогой расчет, вы можете
const expensiveValue = useMemo(() =gt; { // other logic here if needed // could even be simple return var=='x'?true:false, although this would be easier to do in the useEffect hook? return computeExpensiveValue(var); },[var]); useEffect(() =gt; { //do things //expensiveValue only changes when you want it to from the memo }, [expensiveValue])
Ответ №2:
Спасибо вам, самбомартин и Низар, за ваш вклад.
Для всех, кто ищет ответ: После некоторых дальнейших исследований я нашел 3 возможных решения:
- Используйте компонент класса. Если вы действительно зависите от завершения обновления этого состояния, переключитесь на компонент класса, который позволяет вам передавать
setState()
обратный вызов в качестве второго параметра. - Используйте
useRef
крючок, чтобы определить, откуда поступает обновление состояния. Вы можете использовать эту информацию вuseEffect()
методе. - Станьте независимыми от государства. Я использовал это решение и экстернализировал свою функцию обратного вызова с недостатком, заключающимся в том, что при каждом вызове ей присваиваются все параметры, хотя они присутствуют в компоненте, состояния которого сохраняются.
Ответ №3:
Насколько я знаю, эффект использования срабатывает только при изменении значения зависимости, а не просто при выполнении setValue.
Я предлагаю вам три варианта решения, первый, близкий к тому, что вы хотите, но без использования useEffect крюк, второй является продолжением первого, что может потребоваться, если необходимо контролировать состояние, и третий, более общий, как и комментарии говорят, хотя это не будет запущено, если состояние такое же, даже если вы выполняете метод setValue.
- Первое решение: Оберните установленное значение другой функцией, которая определенно контролирует, что может произойти после или до нового состояния:
export default function MyComponent() { const [state, setState] = useState(null); const handleChangeSetState = (nextState, flag) =gt; { if (flag) { specialUseCaseCb(); } setState(nextState); }; return lt;divgt;{/* ... */}lt;/divgt;; }
- Второе решение: Оберните установленное значение другой функцией, как в решении 1, и запросите предыдущее или следующее состояние во внутреннем обратном вызове setState:
export default function MyComponent2() { const [state, setState] = useState(0); const handleChangeSetState = (increment, flag) =gt; setState((prevState) =gt; { const nextState = prevState increment; // you may need prevState or nextState for checking your use case if (flag) { specialUseCaseCb(); } return nextState; }); return lt;divgt;{/* ... */}lt;/divgt;; }
- Третье решение: используйте крючок useEffect для отслеживания изменений, помните, что setState не будет повторно запускать крючок useEffect, если состояние то же самое:
export default function MyComponent3() { const [state, setState] = useState(""); // notice that this will only be triggered if state changes useEffect(() =gt; { if (state !== "my-special-use-case") return; specialUseCaseCb(); }, [state]); return lt;divgt;{/* ... */}lt;/divgt;; }
Комментарии:
1. Спасибо вам за ваш ответ. Первое решение-это то, о чем я подумал с первой попытки. Но в моем случае я хочу дождаться обновленного состояния, а затем выполнить обратный вызов. Если я правильно понял, то в вашем примере это не так, верно?
2. В третьем примере вы будете ждать обновления состояния, чтобы выполнить обратный вызов.
3. Но для содержания состояния используется, чтобы решить, выполнять мой обратный вызов или нет. Я не хотел вносить изменения в структуру государства