在 React 中,定义组件的方式有两种,一个是 class 类组件,一个是函数组件。class 类组件的实现相比于函数组件要复杂。React 组件必须是返回 React 元素的物件,因此无论是函数组件还是类组件都必须有 return React 元素。
import React, { Component } from 'react';
export default class Test extends Component {
constructor(props) {
super(props)
this.state = {
data: {},
}
}
componentDidMount() {
//生命周期函数
}
render() {
return (
这是class组件
)
}
}
props 在类组件内的初始化是在 constructor 函数中进行的,子类必须在constructor方法中调用super方法,否自新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的属
性和方法。如果不调用super方法,子类就得不到this对象。
Hook 出现之前,组件之间复用状态逻辑很难,解决方案都需要重新组织组件结构, 且代码难以理解。组件维护越来越复杂,譬如事件监听逻辑要在不同的生命周期中绑定和解绑,复杂的页面componentDidMount包涵很多逻辑,代码阅读性变得很差,所以hook就为解决这些问题而来。
1.useState的使用
const [state, setState] = useState(initialState);
修改 state值的方法, setState也是异步执行。 可以将setState理解为class组件中的this.setState来修改state变量的值。demo如下:
function Demo() {
const [value, setValue] = useState('初始值')
function changeValue() {
setValue('值已经修改,setValue操作异步执行')
}
return
点击修改值
}
export default Demo;
2.useEffect的使用
useEffect(() => {/**逻辑代码*/}, [/**参数*/])
useEffect可以把 useEffect Hook 看做componentDidMount,
componentDidUpdate和 componentWillUnmount这三个函数的组合。它可以让我们在函数组件中执行副作用操作,默认情况下,它在第一次渲染之后和每次更新之后都会执行,在上述代码中可以用 useEffect 的第二个参数控制useEffect的执行。demo如下:
function Demo() {
const [value, setValue] = useState('初始值')
useEffect(()=>{
console.log(value)//value值发生改变的使用执行useEffect,输出的值为最新值
const timeId = setTimeout(() => {
console.log(“useEffect1-setTimeout-2000”);
}, 2000);
//在Effect中返回一个函数用来清楚定时器,其作用相当于class组件的componentWillUnmount
return () => {
clearTimeout(timeId);
};
},[value])
function changeValue() {
setValue('值已经修改,setValue操作异步执行')
console.log(value)//setValue异步执行,无法同步获得修改后的value的最新值,所以输出非最新值
}
return
点击修改值
}
export default Demo;
effect的回调函数返回一个匿名函数,相当于componentWillUnmount的钩子函数,一般是remove eventLisenter, clear timeId等,主要是组件卸载后防止内存泄漏。
3.useContext的使用
useContext是跨组件共享数据的钩子函数,useContext的组件总会在 context 值变化时重新渲染,demo如下:
const MyContext = React.createContext();
function Demo() {
const [value, setValue] = useState('init')
console.log('demo');
return (
{(() => {
console.log("render");
return null;
})()}
);
}
function Child1() {
const value = useContext(MyContext);
console.log('Child1-value', value);
return Child1-value: {value};
}
function Child2(props) {
console.log('Child2')
return Child2;
}
export default Demo;
上述代码中,Child1函数组件可以获得跨组件useContext中的值,useContext 的组件总会在 context 值变化时重新渲染。
4.useRef的使用
const refContainer = useRef(initialValue);
useRef可以存储那些不需要页面重新渲染的数据。如果你刻意地想要从某些异步回调中读取 /最新的/ state,你可以用 一个 ref 来保存它,修改它,并从中读取。
详情可以查阅官方文档地址:Hook API 索引 – React
5.useReducer的使用
const [state, dispatch] = useReducer(reducer, initialState);
reducer是一个只能通过action将一个函数转化为另一个过程的纯函数,demo:
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case ‘decrement’:
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
>
);
}
上述代码通过点击按钮dispatch传入reducer的action.type,根据不同的状态参数,修改initialState中count的值。
useReducer的使用场景:state 逻辑较复杂且包含多个子值, 可以集中处理;下一个 state 依赖于之前的 state ;想深层级修改子组件的一些状态,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为 你可以向子组件传递 dispatch 而不是回调函数 ;使用reducer有助于将读取与写入分开。
6.useCallback的使用
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
//返回一个 memoized 回调函数。
useCallback将返回一个记忆的回调版本,仅在其中一个依赖项已更改时才更改。当将回调传递给依赖于引用相等性的优化子组件以防止不必要的渲染时,此方法很有用。使用回调函数作为参数传递,每次render函数都会变化,也会导致子组件rerender, useCallback可以优化rerender。
详情请查阅官方文档:Hook API 索引 – React
7.useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
合理利用useMemo可以防止每次render大计算量带来的开销,demo如下:
import React, { useState, useMemo } from “react”;
function Demo() {
const [count, setCount] = useState(0);
const handle = () => {
console.log(“handle”, count);
return count;
};
const handle1 = useMemo(() => {
console.log("handle1", count);
return count;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handle2 = useMemo(() => {
console.log(“handle2”, count);
// 大计算量的方法
return count;
}, [count]);
console.log("render-parent");
return (
demo9: {count}
-------------------
);
}
function Child({ handle }) {
console.log("render-child");
return (
child
props-data: {handle}
);
}
export default Demo;
8.其他Hook
1.useImperativeHandle
useImperativeHandle(ref, createHandle, [deps])
useImperativeHandle可以让你在使用 ref时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。
详情请参阅:Hook API 索引 – React
2.useDebugValue(使用较少)
useDebugValue可用于在 React 开发者工具中显示自定义 hook 的标签。
详情请参阅:Hook API 索引 – React
3.useLayoutEffect(使用较少)
其函数签名与useEffect相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect内部的更新计划将被同步刷新。
详情请参阅:Hook API 索引 – React