1:解决了class组件状态逻辑难以复用的问题
2:解决了class组件复杂难以维护的问题
生命周期函数中包含了很多不相干的逻辑,比如componentDidUpdate中只要一个字段发生变化都会触发,为了优化这种场景会写很多的优化逻辑;
类组件中到处都是对状态的访问和处理,导致组件难以拆分成更小的组件;
3:解决了类组件中this的问题
4:解决了函数组件中不能有自己状态的问题
useState,
const [status, setStatus] = useState(initState);
当使用多个useState的时候必须保证每次他们的调用顺序是一样的;
initState可以是一个函数,用来处理initState需要经过复杂计算得到;
useEffect,
useEffect(() => {
return () => {};
}, [a, b]);
第一个参数相当于class组件的componentDidMount和componentDidUpdate,return值相当于componentWillUnmount,第二个参数是一个数组,表示依赖项,只有依赖项发生改变时,第一个函数参数才会执行;
每次组件重新运行effect都会清除上一个effect,组件卸载的时候也会执行清除副作用的函数;
当第二个参数不传递时,每次组件重新渲染都会重新运行effect(默认是依赖所有);
useEffect会在dom更新完成后才会执行;
useEffect的返回值要么是一个函数要么什么都不返回,不能是async函数,他的返回值是一个promise;
useLayoutEffect,
useLayoutEffect和useEffect函数签名一致,区别是所有的effect会在组件渲染之后浏览器绘制之前同步执行;
useReducer,
const [state, dispatch] = useReducer(reducer, initialArg, init);
reducer类似于redux中的reducer,
const reducer = (state, action) {
switch (action.type) {
case 'increment':
return {
count: state.count + 1};
}
}
initialArg用来初始化state;
如果设置了第三个参数init函数,那么state的初始值会被设置为init(initArg)
dipatch是一个引用类型,会一直保持不变;
useReducer中每次取到的state都是最新的值;
useMemo,
const f2 = useMemo(() => {}, [a, b])
只要a和b的值保持不变,f2的值一直保持不变,一直是第一个入参函数的调用值,第一个入参函数一直不会执行;
useCallback,
const f1 = useCallBack(() => {}, [a, b]);
只要依赖项a和b保持不变,函数f1一直保持不变,也即是f1一直是第一个入参函数
一般用来缓存一些计算量比较大的运算,节省内存
useContext,
接收context对象(React.creatContext({})创建的对象),并返回该对象的当前值;
useContext用来获取context的值并且订阅context的变化;
使用示例
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={
themes.dark}>
<ThemedButton />
</ThemeContext.Provider>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={
{
background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}
useRef
const refContainer = useRef(initialValue);
refContainer保存的是一个可变的值, 在整个组件的生命周期内保持不变,是一个引用类型;
initialValue是refContainer.current初始值;
通过改变refContainer.current的值来解决一些react hooks开发过程中遇到的闭包问题;
useRef+forwardRef+useImperativeHandle
const = useImperativeHandle(ref, createHandle, [deps]);
通过useRef+forwardRef+useImperativeHandle来控制暴露给父组件的ref值;
function Child(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={
inputRef} />;
}
Child = forwardRef(Child);
function Parent() {
const parentRef = useRef();
const handleClick = () => {
parentRef.current.focus();
}
return (
<div>
<Child ref={
parentRef}/>
<Button onClick={
handleClick}>点我</Button>
</div>
);
}
//下面两个hooks是react-redux中提供的hooks
useSelector
import {
useSelector} from 'react-redux';
const state = useSelector(state => state.someState)
useDispatch
import {
useDispatch} from 'react-redux';
const dispatch = useDispatch();
自定义hook:
以use开头并且使用了其他hook
hooks只能在react 函数组件中以及自定义hooks中使用, 不能用在js普通函数以及类组件中;
hooks只能用在函数顶部,保证每次组件渲染时hooks的执行顺序是一样的,不能用在条件语句,循环语句以及嵌套函数中;
todo:
npm install eslint-plugin-react-hooks --save-dev
// 你的 ESLint 配置
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error", // 检查 Hook 的规则
"react-hooks/exhaustive-deps": "warn" // 检查 effect 的依赖
}
}
Hoc:一个纯函数,接收一个组件并返回另一个组件;redux的connect就是通过高阶组件实现的;
withMouse(withPage(MyComponent))
缺点:组件参数容易被串改(组件本身容易被改写,组件参数统一高阶组件覆盖);包装组件参数不知道数据来源比较难调试;
const MyComponent = () => {
return (
{({ x, y }) => (
{({ x: pageX, y: pageY }) => (
{({ api }) => {
// yikes
}}
)}
)}
)
renderProps: 该组件通过属性函数(render或者children)返回一个react元素实现自己的渲染逻辑;react-router是通过renderProps实现的;
缺点:容易造成嵌套地狱;
<Mouse>
{
(position) => {
return <Cat position={
position} />
}
}
</Mouse>
class Mouse extends React.PureComponent {
...逻辑
return (
this.props.children(data)
)
}
https://overreacted.io/zh-hans/making-setinterval-declarative-with-react-hooks
https://juejin.im/post/5dbbdbd5f265da4d4b5fe57d#heading-30