Hook
是React16.8 的新增特性,它可以让你在不编写 class 的情况下使用state以及其他的React特性
Hook API
useState //状态
useEffect //副作用
useContext //上下文
useReducer //Redux
useCallback //回调
useMemo //记忆
useRef //引用
useImperativeHandle //自定义ref
useLayoutEffect //布局副作用
useDebugValue //自定义hook
useState
使用状态
const [n, setN] = React.useState(0)
const [user, setUser] = React.useState({name: 'F'})
不可局部更新
setState不会帮我们合并属性,使用...进行合并
import React, {useState} from "react";
import ReactDOM from "react-dom";
function App() {
const [user,setUser] = useState({name:'Frank', age: 18})
const onClick = ()=>{
setUser({
...user, //合并之前的属性
name: 'Jack' //覆盖之前的属性
})
}
return (
<div className="App">
<h1>{user.name}</h1>
<h2>{user.age}</h2>
<button onClick={onClick}>Click</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
地址要变
setState(obj)如果obj地址不变,那么React就认为数据没有变化,必须是新对象才会更新,
function App() {
const [user,setUser] = useState({name:'Frank', age: 18})
const onClick = ()=>{
user.name = "Jack", //在旧对象上修改数据,React不会更新
setUser(user)
}
useState 可以接受函数
function App() {
const [user,setUser] = useState( ()=>({neme: "Frank", age: 18})
)
该函数返回初始state,且只执行了一次,减少多余的计算过程
setState接受函数
function App() {
const [n, setN] = useState(0)
const onClick = ()=>{
setN(n+1)
setN(n+1) // n 不能加 2,只执行最后一次的操作
setN(i=>i+1) //函数写法,并不知道i的值是多少,只知道i+1这个操作
setN(i=>i+1) //i是占位符,n会自动替换
}
useReducer
用来践行Flux/Redux思想,useReducer是useState的复杂版
创建useReducer,4步
一:创建初始值initialState
二:创建所有操作 reducer(state, action)
三:传给useReducer,得到读和写API
四:调用 写({type:'操作类型'})
useReducer示例代码
import React, {useState, useReducer} from "react";
import ReactDOM from "react-deom";
const initial = { n: 0 }
//把对n的操作写到函数里,第1个参数是旧的状态,2是操作类型,最后返回一个新类型
const reducer = (state, action) =>{
if(action.type === 'add'){
return {n: state.n + action.number}
}else if(action.type === 'mulit'){
return {n: state.n*2}
}else{
throw new Error('unknown type')
}
}
function App(){
//得到读,写API
const {state, dispatch} = useReducer(reducer, initial)
const {n} = state
const onClick = () =>{
//写是调用reducer里的add操作
dispatch({type:'add', number: 1})
};
const onClick2 = () =>{
display({type:'add', number: 2})
}
return (
<div className="App">
<h1>n: {n}</h1>
<button onClick={onClick}>+1</button>
<button onClick={onClick2}>+2</button>
</div>
)
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
使用 useReducer 代替Redux
一:将数据集中在一个store对象
二:将所有操作集中在reducer
三:创建一个Context
四:创建对数据读写API
五:将第四步的内容放在第三步的Context
六:用Context.Provider将Context提供给所有组件
七:各个组件用useContext获取读写API
示例代码
import React, { useReducer, useContext, useEffect } from "react";
import ReactDOM from "react-dom";
//初始化数据,store统一储存仓库
const store = {
user: null,
books: null,
movies: null
};
function reducer = (state, action) => {
switch (action.type) {
case "setUser":
return { ...state, user: action.user };
case "setBooks":
return { ...state, books: action.books };
case "setMovies":
return { ...state, movies: action.movies };
default:
throw new Error();
}
}
const Context = React.createContext(null);
function App() {
const [state, dispatch] = useReducer(reducer, store);
const api = { state, dispatch };
return (
<Context.Provider value={api}>
<User />
<hr />
<Books />
<Movies />
</Context.Provider>
);
}
function User() {
const { state, dispatch } = useContext(Context);
//模拟AJAX,获取用户信息
useEffect(() => {
ajax("/user").then(user => {
dispatch({ type: "setUser", user: user });
});
}, []);
return (
<div>
<h1>个人信息</h1>
<div>name: {state.user ? state.user.name : ""}</div>
</div>
);
}
function Books() {
const { state, dispatch } = useContext(Context);
useEffect(() => {
ajax("/books").then(books => {
dispatch({ type: "setBooks", books: books });
});
}, []);
return (
<div>
<h1>我的书籍</h1>
<ol>
{state.books ? state.books.map(book => <li key={book.id}>{book.name}</li>) : "加载中"}
</ol>
</div>
);
}
function Movies() {
const { state, dispatch } = useContext(Context);
useEffect(() => {
ajax("/movies").then(movies => {
dispatch({ type: "setMovies", movies: movies });
});
}, []);
return (
<div>
<h1>我的电影</h1>
<ol>
{state.movies
? state.movies.map(movie => <li key={movie.id}>{movie.name}</li>)
: "加载中"}
</ol>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
useContext
上下文:全局变量是全局的上下文,上下文是局部的全局变量
使用方法
一:使用C = createContext(initial) 创建上下文
二:使用<C.provider>圈定作用域
三:在作用域内使用 useContext(C) 来使用上下文
useContext不是响应式的,你在一个模块将C里面的值改变,另一个模块不会感知到这个变化
import React, { createContext, useState, useContext } from "react";
import ReactDOM from "react-dom";
//创建Context,初始值空,C就是可以使用的全局变量
const C = createContext(null)
function App(){
const [n, setN] = useState(0);
return (
//圈定作用域,给一个初始值(读和写接口),作用域里面的所有组件都可以使用
<C.Provider value={{n, setN}}>
<div className="App">
<Baba />
</div>
</C.Provider>
);
}
function Baba(){
return (
<div>
我是爸爸 <Child />
</div>
);
}
function Child(){
//使用useContext(C);得到读写接口
const {n, setN} = useContext(C);
const onClick = () =>{
setN(i => i+1);
};
return (
<div>
我是儿子 我得到的 n:{n}
<button onClick={onClick}>+1</button>
</div>
)
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
详细资料点击:React内置Hook API