У меня есть два родственных компонента, которые разделяют состояние через контекст в реакции. Общее состояние между компонентами — это массив.
Если я обновлю состояние arr в одном компоненте, я хочу, чтобы другой компонент прослушивал это обновление и делал что-то соответственно. Когда я использую useEffect во втором компоненте, я слушаю изменения в переменной состояния arr.
Например:
// App Component -------
const App = props => {
const { arr, setArr } = useContext(GlobalContext)
const handleChangeEvent = () => {
const newArr = arr
[10, 20, 30, 40].map(v => {
newArr.push(v)
setArr(newArr)
})
return (...)
}
// App2 (Sibling) Component
const App2 = props => {
const { arr, setArr } = useContext(GlobalContext)
const [localArr, setLocalArr] = useState(0)
useEffect(
() => {
updateLocalState()
},
// fire if "arr" gets updated
[arr]
)
const updateLocalState = () => {
setLocalArr(localArr + 1)
}
return (...)
}
Хук useEffect — это Только, запускаемый при начальном рендеринге, хотя состояние arr обновляется.
Я знаю, что объявление новой переменной const newArr = arr в моей переменной состояния является ссылкой, поэтому newArr.push(v) технически является мутацией состояния. Однако состояние по-прежнему обновляется, никаких предупреждений не выдается, а useEffect ничего не делает.
Почему useEffect не вызывается, хотя состояние обновляется? Это из-за мутации состояния?
Второй вопрос: почему нет предупреждений или ошибок относительно мутации состояния? Мутации состояния опасны. Если это произойдет, я ожидаю какого-то предупреждения.
Живая демонстрация здесь:
Это ссылка на тот же массив, но это не объясняет, почему useEffect не срабатывает, хотя состояние все еще обновляется.
Массив, который вы передаете в качестве второго аргумента в useEffect, только проверяет, являются ли элементы в массиве === элементами в нем в предыдущем рендеринге. const newArr = arr; приведет к newArr === arr, а это не то, что вам нужно.
что говорит @Tholle, просто изменение setArr([...newArr]) запускает useEffect правильно



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Массив, который вы передаете в качестве второго аргумента в useEffect, только проверяет, являются ли элементы в массиве === элементами в нем в предыдущем рендеринге. const newArr = arr; приведет к newArr === arr, поскольку он не создает новый массив, а это не то, что вам нужно.
Создайте новый массив со всеми элементами в arr, и он будет работать как положено.
const App = props => {
const { arr, setArr } = useContext(GlobalContext)
const handleChangeEvent = () => {
const newArr = [...arr]
[10, 20, 30, 40].forEach(v => {
newArr.push(v)
})
setArr(newArr)
}
return <>{/* ... */}</>
}
Я думаю, что, возможно, стоит продемонстрировать лучшую практику для этого блока кода — что-то такое же простое, как setArr([...arr, 10, 20, 30, 40]).
Я не считал, что алгоритм сравнения в useEffect является строгим сравнением и является одним и тем же объектом для этого сравнения. В этом есть смысл.
Интересно, почему не возникает никаких ошибок или предупреждений о мутации состояния?
@lsimonetti У React нет прямого способа проверить наличие мутаций, не говоря уже о предупреждении, когда вы это делаете, к сожалению. Это просто ограничение языка.
Если вы хотите обновить массив с помощью хука useState. Обязательно распространите массив на новый массив и обновите новый массив, чтобы ваш useEffect прослушивал это состояние.
UseEffect не позвонит в приведенном ниже фрагменте кода, поскольку вы напрямую обновляете массив.
const [skills, selectedSkills] = useState([])
const onSelect = (selectedList) => {
selectedSkills(selectedList)
}
useEffect(() => {
MyLogger('useEffect called')
}, [skills])
UseEffect позвоню в приведенном ниже фрагменте кода, поскольку мы сохраняем новую ссылку на массив.
const [skills, selectedSkills] = useState([])
const onSelect = (selectedList) => {
const tempSelectedList = [...selectedList]
selectedSkills(tempSelectedList)
}
useEffect(() => {
MyLogger('useEffect called')
}, [skills])
Вы никогда не создаете новый массив.
const newArr = arrссылается на тот же массив с другой переменной. Попробуйте, например. Вместоconst newArr = [...arr].