系列文章目录
前言
React Hooks
一、函数组件特点
- 没有组件实例
- 没有生命周期
- 没有state 和 setState,只能接收 props
二、useState
默认函数组件没有state
函数组件是一个纯函数,执行完即销毁,无法存储state
需要 State Hook,即把state功能“钩”到纯函数中
- useState(0) 传入初始值,返回数组[state, setState]
- 通过 state 获取值
- 通过 setState(1) 修改值
import React,{useState} from 'react';
function ClickFn() {
// 数组解构
const [count, setCount] = useState(0);
return <div>
<p>{count}</p>
<button onClick={() => setState(count +1)}>点击</button>
</div>
}
export default ClickFn
三、useEffect
import React,{useState, useEffect} from 'react';
function ClickFn() {
// 数组解构
const [count, setCount] = useState(0);
// 模拟 class 组件的DidMount 和 DidUpdate
useEffect(() => {
console.log('发起请求')
})
// 模拟 class 组件的DidMount
useEffect(() => {
console.log('加载完成')
},[]) // 第二个参数是[] (不依赖于state)
// 模拟 class 组件的DidUpdate
useEffect(() => {
console.log('更新了')
},[count]) // 第二个参数是依赖的state
// 模拟 class 组件的willUnMount
useEffect(() => {
let timerId = window.setInterval(() => {
console.log(Date.now())
},1000);
// 返回一个函数
// 模拟willUnMount
return () => {
window.clearInterval(timerId)
}
},[])
return <div>
<p>{count}</p>
<button onClick={() => setState(count +1)}>点击</button>
</div>
}
export default ClickFn
使用总结
模拟componentDidMount - useEffect 依赖
模拟componentDidUpdate - useEffect 无依赖,或者依赖[a,b]
模拟componentWillUnMount - useEffect 中返回一个函数
注意:模拟WillUnMount,但不完全相等
特别注意
此处并不完全等同于WillUnMount
props发生变化,即更新,也会执行结束监听
准确的说:返回的函数,会在下一次 effect 执行之前,被执行
四、useRef
import React,{useRef, useEffect} from 'react';
function UseRef() {
const btnRef = useRef(null); // 初始值
useEffect(() => {
console.log(btnRef.current); // DOM节点
// <button>click</button>
},[]);
return <div>
<button ref={btnRef}>click</button>
</div>
}
export default UseRef
五、useContext
import React,{useContext} from 'react';
const themes = {
light: {
foreground: '#000',
background: '#eee'
},
dark: {
foreground: '#fff',
background: '#222'
}
}
// 创建Context
const ThemeContext = React.createContext(themes.light); // 初始值
function ThemeButton() {
const theme = useContext(ThemeContext);
return <button style={{background: theme.background,color:theme.foreground}}>
hello
</button>
}
function Toolbar() {
return <div>
<ThemeButton></ThemeButton>
</div>
}
function App() {
return <ThemeContext.Provider value={themes.dark}>
<Toolbar></Toolbar>
</ThemeContext.Provider>
}
export default App;
六、useReducer
import React,{useReducer} from 'react';
const initialState = {count: 0};
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return {count: state.count + 1}
case 'decrement':
return {count: state.count - 1}
default:
return state
}
}
function App() {
// 很像 const [count, setCount] = useState(0)
const [state, dispaatch] = useReducer(reducer, initialState);
return <div>
count: {state.count}
<button onClick={() => dispatch({ type:'increment' })}>increment</button>
<button onClick={() => dispatch({ type:'decrement' })}>decrement</button>
</div>
}
export default App
useReducer 和 redux 的区别
1.useReducer 是 useState 的代替方案,用于 state 复杂变化
2.useReducer 是单个组件状态管理,组件通讯还需要props
3.redux 是全局的状态管理,多组件共享数据
七、useMemo
import React, { useState, memo, useMemo } from 'react';
// 子组件
// memo 类似 class组件 PureComponent,对 props 进行浅层比较
const Child = memo(({userInfo}) => {
console.log('Child render');
return <div>
<p>this is child {userInfo.name} {userInfo.age}</p>
</div>
})
// 父组件
function App() {
console.log('parent render...');
const [count, setCount] = useState(0);
const [name, setName] = useState('豆豆');
// const userInfo = {name, age: 20};
// 用 useMemo 缓存数据,有依赖
const userInfo = useMemo(() => {
return { name, age: 21};
},[name])
return <div>
<p>
count is {count}
<button onClick={() => setCount(count + 1)}>click</button>
</p>
<Child userInfo={userInfo}></Child>
</div>
}
export default App;
useMemo 总结
1.React 默认会更新所有子组件
2.class 组件使用 SCU 和 PureComponent 做优化
3.Hooks 中使用useMemo,但优化原理相同
八、useCallback
import React, { useState, memo, useMemo, useCallback } from 'react';
// 子组件
// memo 类似 class组件 PureComponent,对 props 进行浅层比较
const Child = memo(({userInfo,onChange}) => {
console.log('Child render');
return <div>
<p>this is child {userInfo.name} {userInfo.age}</p>
<input onChange={onChange}></input>
</div>
})
// 父组件
function App() {
console.log('parent render...');
const [count, setCount] = useState(0);
const [name, setName] = useState('豆豆');
// const userInfo = {name, age: 20};
// 用 useMemo 缓存数据,有依赖
const userInfo = useMemo(() => {
return { name, age: 21};
},[name]);
// 用 useCallback 缓存函数
const onChange = useCallback(e => {
console.log(e.target.value);
},[])
return <div>
<p>
count is {count}
<button onClick={() => setCount(count + 1)}>click</button>
</p>
<Child userInfo={userInfo} onChange={onChange}></Child>
</div>
}
export default App;
useCallback总结
1.useMemo缓存数据
2.useCallback 缓存函数
3.两者是 React Hooks 的常见优化策略
九、自定义Hook
import { useState, useEffect } from 'react';
import axios from 'axios';
// 封装 axios 发送网络请求的自定义 Hook
function useAxios(url) {
const [loading, setLoading] = useState(false);
const [data,setData] = useState();
const [error, setError] = useState();
useEffect(() => {
// 利用 axios 发送网络请求
setLoading(true);
axios.get(url) // 发送一个 get 请求
.then(res => setData(res))
.catch(err => setError(err))
.finally(() => setLoading(false))
},[url])
return [loading, data, error];
}
export default useAxios;
import React from 'react';
import useAxios from 'useAxios';
function App(url) {
const url='...';
// 数组解构
const [loading, data, error] = useAxios(url);
if(loading) return <div>loading...</div>
return error
? <div>{JSON.stringify(error)}</div>
: <div>{JSON.stringify(data)}</div>
}
export default App;
总结
1.本质是一个函数,以use开头
2.内部正常使用 useState useEffect 或者其他Hooks
3.自定义返回结果,格式不限
十、Hooks使用规范
命名规范 use开头
只能用于React函数组件和自定义Hook中,其他地方不可以
只能用于顶层代码,不能在循环、判断中使用Hooks
eslint 插件 eslint-plugin-hooks 可以帮到你
Hooks 严重依赖于执行顺序
1.函数组件,纯组件,执行完即销毁
2.所以,无论组件初始化(render) 还是组件更新(re-render)
3.都会重新执行一次这个函数,获取最新的组件
4.这一点和 class 组件不一样,class组件初始化实例不会销毁
render: 初始化 state 的值
re-render: 读取 state 的值
render: 添加 effect 函数
re-render: 替换 effect 函数(内部函数也会重新定义)
useHook调用顺序必须保持一致
1.无论是 render 还是 re-render,Hooks调用顺序必须一致
2.如果Hooks出现在循环、判断里,则无法保证顺序一直
3.Hooks严重依赖于调用顺序
十一、class 组件逻辑复用
- Mixins早已废弃(1.变量作用域来源不清 2.属性重名 3.Mixins引入过多会导致顺序冲突)
- 高阶组件 HOC(1.组件层级嵌套过多,不易渲染,不易调试 2.HOC会劫持props,必须严格规范)
- Render Prop(1.学习成本高,不易理解 2.只能传递纯函数,而默认情况下纯函数功能有限)
十二、React Hooks 逻辑复用
- 完全符合 Hooks 原有规则,没有其他要求,易理解记忆
- 变量作用域明确
- 不会产生组件嵌套
十三、React Hooks 注意事项
- useState 初始化值,只有第一次有效(re-render:只恢复初始化的state值,不会再重新设置新的值,只能用 set 修改)
- useEffect 内部不能修改 state
function UseEffectChangeState() { const [count, setCount] = useState(0) // 模拟 DidMount useEffect(() => { console.log('useEffect-',count); // 定时任务 const timer = setInterval(() => { console.log('setInterval-',count); setCount(count + 1); },1000) // 清除定时任务 return () => clearTimeout(timer) },[]) // 依赖是[] // 依赖为 [] 时(didMount):re-render 不会重新执行 effect 函数 // 没有依赖(didMount/didUpdate): re-render 会重新执行 effect 函数 return <div>count: {count} </div> }
- useEffect 可能出现死循环(依赖里有{ } [ ] 引用类型)
总结
React Hooks相关知识点