Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
————可以说之前我们用 Class 声明组件的方式会被淘汰掉
// 1. 引用 useState
import React, { useState } from 'react';
// 2. 声明一个 state(自定义)的 state 变量
const [state, setState] = useState(initialState); // initialState 为初始参数 参数可以是字符串、对象甚至是函数
// 3. 更新 state
setState(newState);
更新状态
import React, { useState } from "react";
import "./styles.css";
function App() {
const [user, setUser] = useState({ name: "Lokka", age: 18 });
const onClick = () => {
setUser({
...user,
name: "XiaoMing"
});
};
return (
<div className="App">
<h1>{user.name}</h1>
<h2>{user.age}</h2>
<button onClick={onClick}>Click</button>
</div>
);
}
export default App;
useEffect 的作用主要是用来解决函数组件如何像类组件一样使用生命周期钩子的问题,在 render 后执行。
// 1. 引用 useEffect
import React, { useEffect} from 'react';
// 2. 创建 useEffect 函数
useEffect(() => {
return () => {
// 清除函数 组件卸载时需要清除 effect 创建的诸如订阅或计时器 ID 等资源。要实现这一点,useEffect 函数需返回一个清除函数
};
},[initialState]); // [initialState] 为 useEffect 第二个参数可以没有,只有当 initialState 改变后会重新调用,也可以写 [],那么就会执行一次
import React, { useState, useEffect } from "react";
function App() {
const [count, setCount] = useState(0);
// 类似于 componentDidMount 和 componentDidUpdate:
useEffect(() => {
// 更新窗口标题用浏览器 API
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
export default App;
与 useEffect 用法相同,不过 useLayoutEffect 在 render 前就会执行,在浏览器绘制完之前就会执行,而 useEffect 在 render 后执行,能用到的场景不多,推荐使用 useEffect
如果我们想在父子组件之间传值的话,可以使用 useContext。
// 1. 引用 useContext
import React, { useContext } from 'react';
// 2. 在父组件创建 context
let ctx = createContext(参数);
// 3. 用 ctx.Provider 包裹子组件,value值为要传给子组件的值
<ctx.Provider value={data}>
<Person />
</ctx.Provider>
// 4. 子组件接收父组件的值
const data = useContext(ctx);
return (
<>
<h1>{data.name}</h1>
</>
);
父子组件传值
import React, { useState, createContext, useContext } from "react";
import "./styles.css";
let ctx = createContext("lokka");
function App() {
let [data, setData] = useState({
name: "小明",
age: 18
});
function change() {
setData({
...data,
name: "LOKKA"
});
}
return (
<div className="App">
<ctx.Provider value={data}>
<Person />
</ctx.Provider>
<button onClick={change}>改变</button>
</div>
);
}
function Person() {
const data = useContext(ctx);
return (
<>
<h1>{data.name}</h1>
</>
);
}
export default App;
useReducer 是用来代替 Redux 的,或者说,是一个加强版的 useState
// 1. 引用 useReducer
import React, { useReducer} from 'react';
// 2. 创建 useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init); // state 是返回的值 dispatch是返回的方法
// reducer 处理函数, initialArg 是初始值 , init 为函数参数,这样初始 state 将被设置为 init(initialArg)
// 3. 触发 dispatch 通过不同的 action.type 得到不同的返回值
dispatch({
type: "setname",
name: "lokka"
})
高级版的 useState,用来代替 Redux
import React, { useReducer } from "react";
import "./styles.css";
const initial = {
n: 0
};
const reducer = (state, action) => {
switch (action.type) {
case "add":
return {
n: state.n + action.number
};
case "multi":
return {
n: state.n * 2
};
default: {
return state;
}
}
};
function App() {
const [state, dispatch] = useReducer(reducer, initial);
const { n } = state;
const onClick = () => {
dispatch({ type: "add", number: 1 });
};
const onClick2 = () => {
dispatch({ type: "add", number: 2 });
};
return (
<div className="App">
<h1>n: {n}</h1>
<button onClick={onClick}>+1</button>
<button onClick={onClick2}>+2</button>
</div>
);
}
export default App;
useMemo 是用来性能优化,防止重复渲染
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
类似与 Vue 的计算属性 computed,useMemo 具有缓存,依赖([a, b])改变才重新渲染的功能,空数组 [] 代表无论什么情况下该函数都不会发生改变
跟它的小弟 useCallback 的唯一区别是:useMemo可以缓存所有对象,useCallback只能缓存函数。
和 useCallback 区别:
useCallback 不会执行第一个参数函数
,而是将它返回给你,而 useMemo 会执行第一个函数
,并且将函数执行结果返回给你。
和 useEffect 区别:
useEffect 没有返回值
,并且是在render之后才执行的,而 useMemo 有返回值
,并且是在页面渲染的时候进行的。
import React, { useState, useMemo } from "react";
import "./styles.css";
function App() {
let [count, setCount] = useState(0);
let res = useMemo(() => count * 10, [count]);
const onClick = () => {
setCount(count + 1);
};
return (
<div className="App">
<h1>{res}</h1>
<button onClick={onClick}>+1</button>
</div>
);
}
export default App;
useCallback(x => log(x), [m]) 等价于 useMemo(() => x => log(x), [m]);
useCallback只缓存函数,不立即执行,直接返回函数
import React, { useState, useCallback } from "react";
import "./styles.css";
function App() {
let [count, setCount] = useState(0);
let res = useCallback(() => count * 10, [count]);
const onClick = () => {
setCount(count + 1);
};
return (
<div className="App">
<h1>{res()}</h1>
<button onClick={onClick}>+1</button>
</div>
);
}
export default App;
useRef
用来生成对 DOM 对象的引用
const refContainer = useRef(initialValue);
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。
useRef 可以通过组件或者 DOM 的 ref 属性,访问组件或真实的 DOM 节点,重点是组件也是可以访问到的,从而可以对 组件 、DOM 进行一些操作,比如获取 input 值、监听事件等等。
当然 useRef 远比你想象中的功能更加强大,useRef 的功能有点像类属性,或者说您想要在组件中记录一些值,并且这些值在稍后可以更改。
import React, { useState, useRef } from "react";
import "./styles.css";
function App() {
let [val, setVal] = useState("p 初始值");
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
setVal(inputEl.current.value);
};
return (
<>
<p>{val}</p>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
export default App;
useImperativeHandle(ref, createHandle, [deps])
获取子组件的方法或者属性
import React, { useRef, useImperativeHandle, forwardRef } from "react";
function ChildInputComponent(props, ref) {
const inputRef = useRef("子组件的LOKKA");
useImperativeHandle(ref, () => inputRef.current);
return <input type="text" name="child input" ref={inputRef} />;
}
const ChildInput = forwardRef(ChildInputComponent);
function App() {
const inputRef = useRef(null);
let focus = () => {
inputRef.current.focus();
};
return (
<div>
子组件:
<ChildInput ref={inputRef} />
<br />
父组件<button onClick={focus}>按钮</button>
</div>
);
}
export default App;
和普通函数本质上没有区别,都是做一些函数的封装,方便使用
封装 hook 复用
简易的计算器:
import React, { useReducer } from "react";
import "./styles.css";
const reducer = (init, action) => {
switch (action.type) {
case "add":
return init + action.num;
case "subtract":
return init - action.num;
case "multiply":
return init * action.num;
case "divide":
return init / action.num;
default: {
return init;
}
}
};
const useCount = (countType, init, num) => {
const [count, dispatch] = useReducer(reducer, init);
const bingo = () => {
dispatch({ type: countType, num: num });
};
return { count, bingo };
};
function App() {
let { count, bingo } = useCount("subtract", 2, 2);
return (
<div className="App">
<h1>{count}</h1>
<button onClick={bingo}>Click</button>
</div>
);
}
export default App;
用来对自定义的 hook
进行调试,强调一下,这个方法使在自定义 hook 中用的,其他 hook 中无效
useDebugValue(value)
import React, { useReducer, useDebugValue } from "react";
import "./styles.css";
const reducer = (init, action) => {
switch (action.type) {
case "add":
return init + action.num;
case "subtract":
return init - action.num;
case "multiply":
return init * action.num;
case "divide":
return init / action.num;
default: {
return init;
}
}
};
const useCount = (countType, init, num) => {
const [count, dispatch] = useReducer(reducer, init);
const bingo = () => {
dispatch({ type: countType, num: num });
};
useDebugValue(count < 0 ? "不能出负数" : "没问题");
return { count, bingo };
};
function App() {
let { count, bingo } = useCount("subtract", 2, 2);
return (
<div className="App">
<h1>{count}</h1>
<button onClick={bingo}>Click</button>
</div>
);
}
export default App;