【问题标题】:useEffect don't fire when the state is modified by a child component through props callback当子组件通过 props 回调修改状态时,useEffect 不会触发
【发布时间】:2020-08-15 04:58:29
【问题描述】:

我有一个 Flatlist 组件,它将其状态和 setState 传递给子组件。子组件确实更改了父组件的状态(尝试使用 setinterval 和 console.log 显示状态)但是当更改发生时我无法收听。我尝试使用

useEffect(()=>{
console.log(`listState:`, state);
},[state]);

但它从不触发,除非在安装时触发。这是我的代码。

// in CheckboxFlatList.js  (parent)
import React, { useState, useEffect, useRef } from 'react';
import { View, StyleSheet, Dimensions, Text } from 'react-native';
import { FlatList } from 'react-native-gesture-handler';
import CheckBox from "./Checkbox";


function CheckboxFlatList(props) {
    const [state, setState] = useState([]);

    useEffect(() => {
        console.log(`listState:`, state);
    },[state]);

    // setInterval(() => {
    //     console.log(`listState:`, state);
    // },3000 );

    return (
        <View>
            <FlatList
                data={data}
                keyExtractor={item => item.id.toString()}
                renderItem={({ item }) => (
                    <View>
                        <View>
                            <CheckBox id={item.id.toString()} listState={state} stateMerge={setState} isChecked={state.some(x => x === item.id.toString())} />
                            <Text>{item.name}</Text>
                        </View>
                    </View>
                )}
            />
        </View >
    );
}
// in Checkbox.js  (child)
import React, { useState, useEffect, useRef, memo } from 'react';
import { CheckBox } from 'react-native-elements';

export default memo(

    function Checkbox(props) {
        let { listState, stateMerge, isChecked, id } = props;
        const [toggleCheckBox, setToggleCheckBox] = useState(isChecked);

        const mounted = useRef();
        useEffect(() => {
            if (!mounted.current) {  // do componentDidMount logic
                mounted.current = true;
            } else {  // do componentDidUpdate logic

                if (toggleCheckBox) {
                    if (!listState.some(x => x === id)) {   //to avoid a duplication bug
                        listState.push(id);
                    }

                } else {
                    while (listState.some(x => x === id)) {   //to avoid a duplication bug
                        let index = listState.indexOf(id);
                        if (index !== -1) listState.splice(index, 1);
                    }
                }
                stateMerge(listState);
            }
        });


        return (
            <CheckBox checked={toggleCheckBox} size={40} onPress={() => {
                setToggleCheckBox(!toggleCheckBox);

            }} />
        );
    });

非常感谢您的帮助!

【问题讨论】:

  • 所以你将一个状态和一个钩子传递给一个记忆化的组件。然后将状态(不使用他的钩子)修改为另一个钩子,使用 useRef 应用 componentDidMount 逻辑,并且有些东西不起作用...... :)
  • @Giovanni_Esposito 我几天前才开始反应原生,这就是原因。你能更详细地向我解释你提到的错误吗?这对我很有帮助。

标签: reactjs react-native


【解决方案1】:

公认的答案是一种临时解决方案,并引入了另一个错误:如果对状态执行无操作更新,则会触发效果。

useEffect 回调未触发,因为 dep 的引用标识未更改。通过重新定义对象来强制使用新的引用身份只会治疗症状。一般来说,您应该着眼于解决根本原因。

在您的情况下,引用身份没有改变是违反 React 状态假设的症状。对状态的更改应始终使用适当的 setter 函数/方法来完成,而不是通过变异来完成。

以下代码块中的违规代码改变了 React 状态。

            if (toggleCheckBox) {
                if (!listState.some(x => x === id)) {   //to avoid a duplication bug
                    listState.push(id);
                }

            } else {
                while (listState.some(x => x === id)) {   //to avoid a duplication bug
                    let index = listState.indexOf(id);
                    if (index !== -1) listState.splice(index, 1);
                }
            }
            stateMerge(listState);

我们可以通过消除突变来修复它。当我们在不使用突变的情况下编写它时,标识自然只有在值发生变化时才会发生变化。

        stateMerge(listState => 
            if (toggleCheckBox) {
                if (!listState.some(x => x === id)) {   //to avoid a duplication bug
                    return [...listState, id];
                } else {
                    return listState;
                }
            } else {
                return listState.filter(x => x !== id);
            }
        );

你应该看到useEffect在状态改变的任何时候触发,而当它没有改变时不会触发。

【讨论】:

    【解决方案2】:

    在你的 Checkbox.js 你调用 stateMerge/setState 回调的地方

    像这样将 listState 传递给 stateMerge

    stateMerge([...listState])
    

    将正确触发useEffect

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-03
    • 2023-01-26
    • 1970-01-01
    • 2020-02-17
    • 2020-06-03
    • 2022-11-16
    • 2019-07-14
    • 2020-11-06
    相关资源
    最近更新 更多