状态:会发生改变的数据
作用(Effect):将数据映射到视图的函数; 4
useCallback
可以缓存一个函数
useMemo
可以缓存一个值,可以减少重绘次数
useContext
可以用来切换状态
useReducer
可以初始化state, 缓存最初的state值;
使用 useRef
可以拿到节点,拿到上一个state缓存的值;
useState
是异步的。
利用自定义Hook可以实现UI和行为的分离;
function useCount(initialValue) {
const [count, setCount] = useState(initialValue);
return [
count,
() => {
setCount(count + 1)
}
];
}
export default () => {
const [count1, addCount1] = useCount(0);
const [count2, addCount2] = useCount(0);
return (
<div>
Your count: {count1}
Your count: {count2}
<button onClick={() => addCount1()}>Add1</button>
<button onClick={() => addCount2()}>Add2</button>
</div>
)
}
一、useContext
context可以作为一个知识点,在所有的组件中使用;通过useContext来实现。
import React, { useContext, useState } from 'react';
// 存储颜色的对象,里面有两种颜色
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
// 创建一个context
const ThemeContext = React.createContext({
theme: themes.light,
toggle: () => {}
});
export default function App() {
const [theme, setTheme] = useState(themes.light);
return (
// ThemeContext.Provider: 提供者
// 在这个组件之下的所有组件都可以使用这个context
<ThemeContext.Provider
value={{
theme,
toggle: () => {
setTheme(theme => {
setTheme(theme === themes.light ? themes.dark: themes.light)
});
}
}}
>
<Toolbar />
</ThemeContext.Provider>
);
}
const Toolbar = () => {
return <ThemedButton />;
}
const ThemedButton = () => {
const context = useContext(ThemeContext);
return (
<button
style=
{{
background: context.theme.background,
color: context.theme.foreground
}}
onClick={() => {
context.toggle();
}}
>
I am styled by theme context!
</button>
);
}
二、useReducer
reducer是一个设计模式,提供了一种抽象状态行为的通用封装(action),以及计算过程的抽象方案。
reducer将很多action映射成state的一个函数
import React, { useReducer } from "react";
const initialState = {count: 0};
function reducer(state: any, action: any) {
console.log('state', state);
console.log('action', action);
switch (action.type) {
case 'add':
return { count: state.count + 1};
case 'sub':
return { count: state.count - 1};
default:
throw new Error();
}
}
export default function Counter() {
const [state, dispatch] = useReducer(reducer, initialState); // state的初始值是initialState
return (
<>
Your counter is: {state.count}
<button onClick={() => dispatch({type: 'add'})}>+</button>
<button onClick={() => dispatch({type: "sub"})}>-</button>
</>
)
}
希望增加init功能:惰性初始化;
reducer Hook的返回值与当前state相同,则跳过子组件的渲染及副作用的执行;(React通过object.is比较算法来比较state)
import React, { useReducer } from "react";
const initialCount = {count: 0};
function init(initialCount: any) {
return {count: initialCount.count};
}
function reducer(state: any, action: any) {
console.log('state', state);
console.log('action', action);
switch (action.type) {
case 'add':
return { count: state.count + 1};
case 'sub':
return { count: state.count - 1};
case 'reset':
return init(action.payload);
default:
throw new Error();
}
}
export default function Counter() {
const [state, dispatch] = useReducer(reducer, initialCount, init);
// state的初始值是initialCount
return (
<>
Your counter is: {state.count}
<button onClick={() => dispatch({type: 'reset', payload: initialCount})}>reset</button>
<button onClick={() => dispatch({type: 'add'})}>+</button>
<button onClick={() => dispatch({type: "sub"})}>-</button>
</>
)
}
三、useRef
使用 useRef
可以拿到节点
import React, { useRef } from "react";
export default function useRefExample() {
const refInput = useRef()
return (
<div>
<input ref={refInput} />
<button onClick={() => {
refInput.current.focus(); // 点击button,输入框聚焦
}}>
Focus
</button>
</div>
);
}
使用 useRef
拿到上一个状态的值
import React, { useState, useRef } from "react";
export default function useRefExample() {
const [counter, setCounter] = useState(0);
const prev = useRef(null);
return (
<div>
<p>当前值: {counter}</p>
<p>之前的值: {prev.current}</p>
<button
onClick={() => {
prev.current = counter;
setCounter(x => x + 1);
}}
>
Click me to add
</button>
<button
onClick={() => {
prev.current = counter;
setCounter(x => x - 1);
}}
>
Click me to sub
</button>
</div>
);
}
缓存? 什么是缓存?
缓存一个函数(useCallback)
缓存一个值(useMemo),可以减少重绘次数
import React, { useState, useMemo } from "react";
export default function UseMemoExample() {
const [count, setCount] = useState(0);
const memorizedText = useMemo(() => {
console.log('run useMemo function');
return `this is a memorized text ${Date.now()}`;
}, []);
// 依赖是[]中的内容,内容是空的,永远不会变,所以只被计算一次
return (
<div>
{memorizedText}
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
const memorizedText = useMemo(() => {
console.log('run useMemo function');
return `this is a memorized text ${Date.now()}`;
}, [Math.floor(count / 10)]);
// 依赖是[]中的内容,只要内容发生变化,就会执行useMemo;
四、useCallback
下面这种操作,每次调完 setCount
,整个函数都会重新执行一遍,会创建一个新的 add
函数。会消耗性能,但是消耗的很少。
import React, { useState, useCallback } from 'react';
const s = new Set();
export default () => {
const [count, setCount] = useState(0);
const onAdd = () => {
setCount(x => x + 1);
}
s.add(onAdd);
console.log(s.size); // 1 2 3 4 ...
return (
<div>
{count}
<button onClick={onAdd}>+</button>
</div>
)
}
为了解决这个性能问题,可以使用 useCallback
import React, { useState, useCallback } from 'react';
const s = new Set();
export default () => {
const [count, setCount] = useState(0);
const onAdd = useCallback(() => {
setCount(x => x + 1);
}, [])
s.add(onAdd);
console.log(s.size);
// 不论点击多少次,一直都是1,说明只有一个函数
return (
<div>
{count}
<button onClick={onAdd}>+</button>
</div>
)
}
五、useEffect
和 useLayoutEffect
的区别
useEffect
是异步执行的,浏览器完成渲染之后执行
useLayoutEffect
是同步执行的,浏览器把真正的内容渲染到页面之前执行