当子组件通过props回调修改状态时,useEffect不会触发

时间:2020-08-14 18:53:59

标签: reactjs react-native

我有一个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);

            }} />
        );
    });

非常感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

可接受的答案是一个创可贴解决方案,并引入了另一个错误:如果对该状态执行无操作更新,则会触发效果。

由于dep的引用身份未更改,因此未触发 services.AddTransient<IRestClient, RestClient>(); services.AddSingleton<IClient, Client>(); 回调。通过重新定义对象来强制使用新的参照身份仅能解决该症状。通常,您应该着眼于根本原因。

在您的情况下,引用身份未更改是违反React状态假设的征兆。应该始终使用适当的setter函数/方法来完成状态更改,而不要通过更改来完成。

以下代码段中令人反感的代码会改变React状态。

useEffect

我们可以通过消除突变来解决它。当我们在不使用突变的情况下编写代码时,身份自然只会在值更改时发生更改。

            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); } ); 被触发,而状态未更改时则不触发。

答案 1 :(得分:0)

在Checkbox.js中调用stateMerge / setState回调的位置

像这样将listState传递给stateMerge

stateMerge([...listState])

将正确触发useEffect