React hooks 如何实现生命周期

297 阅读2分钟

为什么需要hooks实现声明周期


函数组件本身是无状态的,因此如果想要像类组件一样使用生命周期等特性,需要接入hook来实现,那么hooks如何实现类组件中的生命周期呢?

具体生命周期


  1. 类组件中的constructor构造函数 ⇒ useState()。类组件中,构造函数会在实例化时为组件初始化状态;函数组件第一次使用useState()时可以记录state到fiber实现状态初始化。
class Example extends Component {
    constructor() {
        super();
        this.state = {
            data: 0
        }
    }
    render() {
      return <div></div>;
	  }
}

function Example() {
  const [data, setData] = useState(0);
  return <div></div>;
}
  1. componentDidMount ⇒ useEffect(() ⇒ {}, [])。传给 useEffect 的函数会在浏览器完成布局与绘制之后(不会阻塞渲染) ,在一个延迟事件中被调用。函数组件的useEffect在不提供依赖时表示只运行一次的 effect(仅在组件挂载和卸载时执行)。
class Example extends Component {
    componentDidMount() {
        console.log('mounted');
    }
    render() {
      return <div></div>;
	  }
}

function Example() {
  useState(() => {
		console.log('mounted');
	}, []);

  return <div></div>;
}
  1. shouldComponentUpdate ⇒ React.memo()。shouldComponentUpdate能够对比props和state的变化;React.memo()可以对比props,而函数组件中还可以利用useMemo来缓存state。

React.memo()只是浅比较props,如果函数组件中存在useState、useReducer、useContext,当state或context变化时依然还会重新渲染。

class Example extends Component {
    shouldComponentUpdate() {
        console.log('shouldComponentUpdate');
    }
    render() {
      return <div></div>;
	  }
}

function _Example() {
  useState(() => {
		console.log('mounted');
	}, []);

  return <div></div>;
}
const Example = React.memo(
	_Example,
	(prevProps, nextProps) => nextProps.count !== prevProps.count
)
  1. componentDidUpdate ⇒ useEffect(() ⇒ {}) + useRef。useEffect没有指定第二个依赖项的参数时,会在每次渲染结束后执行,虽然模拟了componentDidUpdate,但是也在componentDidMount时触发了,因此借用useRef来记录是否进行过第一次渲染。
class Example extends Component {
    **componentDidUpdate**() {
      console.log('**componentDidUpdate'**);
    }
    render() {
      return <div></div>;
  }
}

function Example() {
	const didMount = useRef(false);

  useEffect(() => { 
		if(didMount.current) {
			console.log('**componentDidUpdate');**
		} else {
			console.log('**componentDidMount');
			didMount.current = true;**
		}
	****});
  return <div></div>;
}
  1. componentWillUnmount ⇒ useEffect(() ⇒ {}, [])。通过为useEffect中return卸载函数,可以指定组件卸载时的操作。
class Example extends Component {
    componentWillUnmount() {
      console.log('componentWillUnmount**'**);
    }
    render() {
      return <div></div>;
  }
}

function Example() {
	const didMount = useRef(false);

  useEffect(() => { 
		return () => {
			console.log('componentWillUnmount');
		}
	****}, []);
  return <div></div>;
}
  1. getDerivedStateFromProps ⇒ 函数组件“render”过程中重新设定state。重新设定state会使 React 立即退出第一次渲染并用更新后的 state 重新运行组件以避免耗费太多性能。

getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。

function ScrollView({row}) {
  const [isScrollingDown, setIsScrollingDown] = useState(false);
  const [prevRow, setPrevRow] = useState(null);

  if (row !== prevRow) {
    // Row 自上次渲染以来发生过改变。更新 isScrollingDown。
    setIsScrollingDown(prevRow !== null && row > prevRow);
    setPrevRow(row);
  }

  return `Scrolling down: ${isScrollingDown}`;
}

参考


  1. 生命周期方法要如何对应到 Hook?: zh-hans.reactjs.org/docs/hooks-…