React中使用useState()导致的问题记录

https://www.jianshu.com/p/d9158074176b

场景一: 更新 state 的一个对象(或数组)属性的某个子属性或值。

使用 Hook Function Component

function App() {
    const [arr, updateArr] = useState([]);
    const addList = () => {
        arr.push('Hello React');
        updateArr(arr);
    };
    return (
        <div>
            {
                arr.map((item, index) => (
                    <p key={index}>{index} {item}</p>
                ))
            }
            <button onClick={addList}>添加List</button>
        </div> 
    );
}

使用 Class Component

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            arr: []
        }
    }

    addList = () => {
        let arr = this.state.arr;
        arr.push('Hello React');
        this.setState({
            arr: arr
        }, () => {
            console.log(this.state.arr);
        });
    };

    deleteList = () => {
        const { arr } = this.state;
        arr.splice(0, 1);
        this.setState({
            arr: arr
        }, () => {
            console.log(this.state.arr);
        });
    };

    render() {
        const { arr } = this.state;
        return (
            <div>
                {
                    arr.map((item, index) => (
                        <p key={index}>{index} {item}</p>
                    ))
                }
            <button onClick={this.addList}>添加List</button>
            <button onClick={this.deleteList}>删除List</button>
        </div> 
        );
    }
}

结果:使用 Hook Function Component push 数组后数组长度并没有改变,使用Class Component正常。
原因:在 Hook 中直接修改 state 的一个对象(或数组)属性的某个子属性或值,然后直接进行 set,不会触发重新渲染。

  • 对 Class Component来说,state 是 Immutable 的,setState 后一定会生成一个全新的 state 引用。它是通过 this.state 方式读取 state,所以每次代码执行都会拿到最新的 state 引用。
  • 对 Hook Function Component 而言,useState 产生的数据也是 Immutable 的,通过数组第二个参数 Set 一个新值后,原来的值会形成一个新的引用在下次渲染时。

解决方案

改变引用地址

function App() {
    const [arr, updateArr] = useState([]);
    const addList = () => {
        arr.push('Hello React');
        // 通过扩展运算符实现深拷贝
        updateArr([...arr]);
    };
    return (
        <div>
            {
                arr.map((item, index) => (
                    <p key={index}>{index} {item}</p>
                ))
            }
            <button onClick={addList}>添加List</button>
        </div> 
    );
}

场景二: 在setTimeout中更改state。

使用 Hook Function Component

function App() {
    const [count, updateCount] = useState(0);

    useEffect(() => {
        let timer = setTimeout(() => {
            updateCount(1);
            getCount();
        }, 1000);
        return () => {
            clearTimeout(timer);
        }
    }, []);

    const getCount = () => {
        console.log(count); // result: 0
    };
    
    return (
        <div>{count}</div> 
    );
}

使用 Class Component

let timer = null;
export default class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
    }

   componentDidMount() {
        timer = setTimeout(() => {
            this.setState({
                count: 1
            })
            this.getCount();
        }, 1000);
   }


   getCount = () => {
        console.log(this.state.count); // result: 1
   }

   componentWillUnmount() {
        clearTimeout(timer);
   }

    render() {
        const { count } = this.state;
        console.log(count); // result: 1
        return (
            <div>{count}</div> 
        );
    }
}

结果:使用 Hook Function Component 更改count后,页面显示1,getCount方法中打印的count为0,使用Class Component更改count后页面显示1,getCount方法中打印的count为1。
原因:Hook Function Comoponent中由于对 state 的读取没有通过 this. 的方式,使得每次 setTimeout 都读取了当时渲染闭包环境的数据,虽然最新的值跟着最新的渲染变了,但旧的渲染里,状态依然是旧值。

解决方案

使用ref

function App() {
    const [count, updateCount] = useState(0);

    useEffect(() => {
        let timer = setTimeout(() => {
            updateCount(1);
            getCount();
        }, 1000);
        return () => {
            clearTimeout(timer);
        }
    }, []);

    let ref = useRef();
    ref.current = count;
    const getCount = () => {
        console.log(ref.current); // result: 1
    };
    
    return (
        <div>{count}</div> 
    );
}

The End~

附上我认为比较值得研读的相关文章:

1.react-hook-usestate-setState
2.精读《Function Component 入门》

posted @ 2020-11-17 13:44  simple-love  阅读(1011)  评论(0编辑  收藏  举报