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

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

使用 Hook Function Component

function App() {
    const [arr, updateArr] = useState([]);
    const addList = () => {
        arr.push('Hello React');
        updateArr(arr);
    };
    return (
        
{ arr.map((item, index) => (

{index} {item}

)) }
); }

使用 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 (
            
{ arr.map((item, index) => (

{index} {item}

)) }
); } }

结果:使用 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 (
        
{ arr.map((item, index) => (

{index} {item}

)) }
); }

场景二: 在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 (
        
{count}
); }

使用 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 (
            
{count}
); } }

结果:使用 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 (
        
{count}
); }

The End~

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

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

你可能感兴趣的:(React中使用useState()导致的问题记录)