react hooks
是react 16.8.0
版本开始推广的。
class声明的组件
我们称之为有状态组件,function声明的组件
我们称之为无状态组件。
对于冗长难以复用的有状态组件,很容易陷入多层级状态嵌套问题,react提出了2种解决方案。
渲染属性:使用一个函数类型的props值来传递子组件,让组件更加精炼明细。
高阶组件:通过函数传入一个组件,返回一个新的组件,达到组件的细化。
hooks的出现就是解决状态共享问题,在上面2种解决方案的下再次推广一种新的状态解决方案–函数型组件
函数类型的组件写法平铺,易于将UI和状态分离。
可根据依赖进行状态变化的实时处理,避免了大量的优化操作。
无状态函数组件本身并不具备状态和生命周期,它可以做到最小化设计,并且方便复用和独立测试。
react为了让组件更加具有复用性,提出了react hook,其解决了原始函数组件不具备自身状态,生命周期,this指向等问题,并且优化了部分有状态组件的手工操作。hooks相互间的引用也是基于函数级的,更加便捷。
React hooks只是为了让函数类型的组件可以便捷复用,与class类型的组件并无明显的差异,2者都可通过自己的方式实现组件细化和封装。
const [state, setState] = useState(initialState);
相当于类组件的
this.state
,但是useState
可以接受任意类型的参数,而this.state={}
参数:initialState
,任意类型的数据
返回值:一个数组,包含2个值。第一个值是当前state的值,第二个值是一个函数,可以用于设置当前state的值。
第二个返回值
setState((prevState)=>{ return newState})
。其接受上一次状态值作为参数,返回一个新的值。相当于class中的setState({})
,但是useState的第二返回值,并不会合并state,而是直接替换上一次的值。
- setState完成后会直接进行render,但是在render外获取state的值并不会改变,需要配合useEffect监听该状态的改变才能正常使用
注意:
useEffect(()=>{
//逻辑处理
//组件卸载
//return ()=>{}
},[stateNameArr])
useEffect
又称为副作用,主要用于替代class的生命周期。每次render后都会调用该函数。
参数:
第一参数:function,定义逻辑处理。允许返回一个回调函数,该回调函数会在组件卸载的时候执行。
第二参数:array,定义哪些状态改变会调用该方法的第一参数,执行其逻辑。
这里系统内部处理了前后两次状态的比较,如果有差异才会执行,否则跳过。
- 如果参数是个空数组,则相当于
componentDidMount
生命周期- 如果数组中有值,则相当于
componentDidUpdate
生命周期,且数组中定义的任意状态的改变都将被触发。
返回值:一个回调函数,非必须。如果存在,则在组件卸载时执行componentWillUnmount
生命周期。
import React, { useState, useEffect } from 'react';
import { Button } from 'antd';
interface TestProps { };
const Test: React.FC = ({ }) => {
const [count, setCount] = useState(0);
const [addClickNum, setAddClickNum] = useState(0);
const [title, setTitle] = useState('React Hooks');
useEffect(()=>{
//初始化项目,相当于componentDidMount
let titleDom = document.getElementById("title");
titleDom.innerText = "React Hooks Title"
},[])
useEffect(()=>{
//当count改变时进入,相当于componentDidUpdate
let titleDom = document.getElementById("title");
titleDom.innerText = "React Hooks " + count;
//如果有返回函数则相当于componentWillUnmount
return ()=>{
console.log("卸载组件")
}
},[count])
const addCount = () => {
setCount(count => ++count);
setAddClickNum((state) => ++state)
}
const subCount = () => {
setCount(count => --count);
}
return (
{title}
统计数:{count}
添加点击数:{addClickNum}
);
};
export default Test;
这我们发现我们定义了2次
useEffect
,一次相当于初始化,另一次表示count值改变时调用。实际上只要定义了useEffect
,无论第二参数有值无值,都经会执行一次初始化操作,而非有值的情况下只进行更新逻辑。所以我们会发现页面的标题只显示第二次的设置,第一次的定义被覆盖掉了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FiAKADDh-1589973766532)(/Users/windeye/Desktop/图片/effect_hooks.png)]
const refContainer = useRef(initialValue);
相当于class组件中的ref属性,但是它区别于ref在于它不是一个纯的dom元素或者实例,而是一个js对象。其
current
属性代表dom元素或者实例。
参数:任意值,可用于存储跨生命周期的变量。
返回值:一个对象,仅包含一个current
属性。
注意事项:
current
属性也不会引发组件重新渲染。useImperativeHandle(ref, createHandle, [deps])
seImperativeHandle
可以让你在使用 ref
时自定义暴露给父组件的实例值。
useImperativeHandle
需要与forwardRef
配套使用。
const refComponent = React.forwardRef((props,ref)=>{return ...})
forwardRef会创建一个组件,并将组件接收的ref属性传递到其任意子组件。从而达到父组件可以直接调用子组件的方法实例。
import React, { useState, useEffect, useRef, forwardRef, useImperativeHandle } from 'react';
import { Button } from 'antd';
const Title: any = ((props, ref) => {
const [title, setTitle] = useState('React Hooks');
//第二个参数为一个返回一个对象,其可以自定义ref的属性
useImperativeHandle(ref, () => ({
getTitle: () => {
return title
}
}))
return ({title}
)
})
//搭配forwardRef使用
const FcTitle = forwardRef(Title);
interface TestProps { };
const Test: React.FC = ({ }) => {
const [count, setCount] = useState(0);
const [addClickNum, setAddClickNum] = useState(0);
const testRef = useRef(null);
useEffect(() => {
if (testRef.current) {
let title = testRef.current.getTitle()
console.log(title)
}
}, [])
const addCount = () => {
setCount(count => ++count);
setAddClickNum((state) => ++state)
}
const subCount = () => {
setCount(count => --count);
}
return (
统计数:{count}
添加点击数:{addClickNum}
);
};
export default Test;
const callBack = useCallBack(fn,deps)
参数:
返回值:一个memoized回调函数。
常用于定义传递给子组件的函数,因为函数组件无法绑定this,所有可以根据回调函数的形式传递给子组件。
注意事项:
const memoValue = useMemo(fn,deps)
相当于
const memoValue = useCallBack(()=>fn,deps)
参数:
返回值:一个memoized值。
useMemo会立即执行回调函数,并返回其结果值。
注意事项:
useMemo
shouldComponentUpdate
的替代方案。const [state, dispatch] = useReducer(reducer, initialState, init);
useReducer
是useState
的替代方案,对于多层级组件的传值,帮助较大。类似于redux的流程处理。
参数:
const reducer = (state,action)=>{...}
,接收一个reducer纯函数,返回一个新的state。const init = (initialState)=>{}
,用于惰性初始化,方便重置操作。返回值:
state
,当前状态dispath
,转发函数,接收一个对象参数{type:actionName}
注意事项:
useReducer
的dispatch
来模拟forceUpdate
,实质也是状态改变,然后重绘组件。import React, { useReducer } from 'react';
import { Button } from 'antd';
interface CountProps {
state: any,
dispatch: any
};
const Count: React.FC = ({ state, dispatch }) => {
return (
统计数:{state.count}
)
}
interface TestProps { };
const Test: React.FC = ({ }) => {
//定义reducer
const reducer = (state, action) => {
switch (action.type) {
case 'add':
return { count: state.count + 1 }
case 'sub':
return { count: state.count - 1 }
}
}
//初始化状态
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
React Hooks
);
};
export default Test;