将componentDidMount转换为useEffect

时间:2020-10-17 12:31:41

标签: javascript reactjs react-hooks use-effect react-component

我试图学习React钩子,并试图将现有的代码库转换为使用钩子,但是我很困惑。 在useEffect内设置状态是否正常?如果这样做,是否会引起可怕的无限循环?

import React, { useState, useEffect } from 'react';
import App from 'next/app';
import Layout from './../components/Layout';

function MyApp({ Component, pageProps }) {
    const [ cart, setCart ] = useState([]);

    const addToCart = (product) => {
        setCart((prevCartState) => {
            return [ ...prevCartState, product ];
        });
        localStorage.setItem('cart', JSON.stringify(cart));
    };

    //mimicking componentDidMount to run some effect using useEffect
    //useEffect(() => setCount((currentCount) => currentCount + 1), []);
    useEffect(() => {
        const cartCache = JSON.parse(localStorage.getItem('cart'));
        //cart = cartCache; Good or bad?
        cartCache || setCart(() =>{

        });        
    }, []);
    return <Component {...pageProps} />;
}

我原始的基于类的组件:

/*
export default class MyApp extends App {
  state = {
    cart: []
  }
  componentDidMount = () => {
    const cart = JSON.parse(localStorage.getItem('cart'));
    if (cart) {
      this.setState({
        cart
      });
    }
  };
  addToCart = (product) => {
    this.setState({
      cart: [...this.state.cart, product]
    });
    localStorage.setItem('cart', JSON.stringify(this.state.cart));
  }
  render() {
    const { Component, pageProps } = this.props
    return (
      <contextCart.Provider value={{ cart: this.state.cart, addToCart: this.addToCart }}>
        <Layout>
          <Component {...pageProps} />
        </Layout>
      </contextCart.Provider>
    )
  }
}*/

2 个答案:

答案 0 :(得分:2)

只要您不侦听依赖项数组中同一字段的更改,就可以在useEffect中设置状态是可以的。在您的特定情况下,您仅调用一次useEffect(因为您已经传递了一个空的依赖项数组)。

useEffect(() => {
   const cartCache = JSON.parse(localStorage.getItem('cart'));
   if (cartCache) {
      setCart(cartCache);   
   }     
}, []);

添加第二个useEffect来收听cart的更改并保持localStorage为最新状态也很酷。

useEffect(() => {
   localStorage.setItem('cart', JSON.stringify(cart));
}, [cart]);

答案 1 :(得分:2)

由于localStorage.getItem()同步调用,因此在这种情况下,您也可以使用useState的函数回调版本来设置初始值。这样,您无需使用useEffect。通常,如果可能的话,我会尽量避免在功能组件中引入任何新的副作用。

您可以尝试以下操作:

const [ cart, setCart ] = useState(() => {
   const cartCache = JSON.parse(localStorage.getItem('cart'));

   if (cartCache) {
      return cartCache;
   }

   localStorage.setItem('cart', JSON.stringify([]));
   return [];
});

这样,如果cart中缺少localStorage元素,则代码将使用默认的[]空数组创建该元素,并将其设置为您的状态。在其他情况下,它将通过存储状态来设置状态。

请注意:另外,我在听cart状态更改方面与the answer from kind user here保持一致,以使localStorageuseEffect保持最新。我的建议仅针对初始状态。