Hook
是 React 16.8
中的新增功能。它们容许您在不编写类的状况下使用状态和其余 React
功能。HOOKS
只能在函数组件中使用react
React.memo
是一个高阶的组件。它相似于 React.PureComponent
也就是说若是组件的 props
没有改变,是不会被从新渲染的。git
function Foo (props) {
...
}
export default React.memo(Foo)
复制代码
React.PureComponent
如何实现性能优化的咱们都知道类组件的中有个 shouldComponentUpdate
生命周期函数。当这个函数返回 false
时,表示组件不会从新渲染;当返回 true
时,表示组件会从新渲染。PureComponent
组件就是在 shouldComponentUpdate
函数中对组件接受的 props
进行比较(props
和 nextProps
进行比较)若是发生变化就返回 true
。同时还有自身的 state
数据也会进行一个比较(state
和 nextState
),若是发生变化就返回 true
。若是上面两种数据的个自比较都返回 false,那么组件就不会发生渲染,减小没必要要的渲染,从而达到性能的优化。推荐一篇文章React PureComponent 源码解析你们本身看看github
React.PureComponent
有什么区别React.memo
为高阶组件。它与 React.PureComponent
很是类似,但它适用于函数组件,但不适用于 class
组件。若是你的函数组件接受了一个彻底相同的 props
那么 memo
就不会使函数组件发生从新渲染。可是 memo
并不会对函数组件自身的 state
数据进行一个浅比较(useReducer
和 useState
钩子返回的数据)。因此二者在做用上仍是有一些区别的。segmentfault
相似于类组件中的state,不一样的是 useState
接受一个任意类型的值 string, array, object, bool...
做为参数并返回一个数组,且 useState
只会在组件初始化的时候执行。数组
// 初始化的时候,age的值就是useState中参数的值
const [ age, setAge ] = useState(20);
const [ visible, setVisible ] = useState(props.visible);
复制代码
数组中的第一个元素是状态值,组件在运行过程当中会保留这个状态值,相似于 this.state
数组中的第二个元素是改变这个状体值的函数,相似于 this.setState()
性能优化
function Hooks(props) {
const [ age, setAge ] = useState(20);
const [ visible, setVisible ] = useState(props.visible);
return (
<div className=""> <p>个人年龄是{age}岁</p> <button onClick={() => setAge(age + 1)}>点击</button> <p>{`${visible}`}</p> </div>
);
};
复制代码
咱们能够在函数组件中屡次使用 useState
,来建立多个状态值供咱们使用。可是,必须在函数做用域的最顶层使用 useState
,不能嵌套在循环内部或者其余函数做用域内部或者是块级做用域中。app
对于使用过类组件的同窗来讲,咱们能够理解为 useEffect
是类组件中 componentDidMount
和 componentDidUpdate
两个生命周期的一个集合。每次当函数组件挂载成功或者从新渲染完成后都会调用 useEffect
。 可是也有不一样的地方,useEffect
不彻底同类组件中的 componentDidMount
和 componentDidUpdate
生命周期函数同样,useEffect
有延迟,在父组件 didMount 或 didUpdate 后,但在任何新渲染以前触发。useEffect
能够在组件中使用屡次, 和 useState
使用同样。dom
useEffect
还能够返回一个函数,并在组件即将销毁时调用这个返回函数,没错,就是和类组件的 componentWillUnmount
同样。咱们经过它来取消在 useEffect
中绑定的事件监听等行为。ide
通常状况下,咱们能够将一个行为事件的绑定和取消绑定放在同一个 useEffect
中,这样代码的可读性和维护行会更强一些。函数
function Hooks(props, ref) {
const boxRef = useRef(null);
useEffect(() => {
function handle() {
console.log(123456)
}
boxRef.current.addEventListener('click', handle, false);
return () => {
boxRef.current.removeEventListener('click', handle, false);
}
}, []);
return (
<div ref={boxRef}> 12344556 </div>
);
}
复制代码
经过下面的这张图,能够看出来 useEffect
的有一个延迟
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新
复制代码
上面这个示例中,咱们传入 [count]
做为第二个参数。这个参数是什么做用呢?若是 count
的值是 5,并且咱们的组件重渲染的时候 count
仍是等于 5,React
将对前一次渲染的 [5]
和后一次渲染的 [5]
进行比较。由于数组中的全部元素都是相等的(5 === 5),React 会跳过这个 effect
,这就实现了性能的优化。
当渲染时,若是 count
的值更新成了 6,React
将会把前一次渲染时的数组 [5] 和此次渲染的数组 [6] 中的元素进行对比。此次由于 5 !== 6,React
就会再次调用 effect
。若是数组中有多个元素,即便只有一个元素发生变化,React
也会执行 effect
。
若是参数中有多个元素 [ age, props.visible ]
,组件渲染时经过比较后只要有一个元素发生变化,useEffect
就会执行。若是参数是一个空数组 []
,那么这个时候 useEffect
就和类组件中的 componentDidMount
同样,只在组件挂载成功后调用一次。 useEffect
函数中 return
的函数,不受第二个参数的影响,仍在组件即将销毁的时候调用。
其实,当第二个参数不是空的数组时,useEffect
也会在组件挂载成功后调用一次,这一点不能忘记。
不要在循环条件或嵌套函数中调用 Hook
。相反,始终在 React
函数的顶层使用 Hooks
。经过遵循此规则,您能够确保每次组件呈现时都以相同的顺序调用Hook
。这就是React
容许多个 useState
和 useEffect
调用之间正确保留 Hook
状态的缘由。
和 useEffect
使用原理相同,可是惟一的区别在于 useLayoutEffect
不会延迟触发,和类组件的 componentDidMount
和 componentDidUpdate
这两个生命周期函数基本处于同步的状态。
自定义 Hook
是一个 JavaScript
函数,其名称以 "use" 开头,能够调用其余 Hook
。构建本身的 Hook
能够将组件逻辑提取到可重用的函数中 ,确保只在自定义 Hook
的顶层无条件地调用其余 Hook
。与 React
组件不一样,自定义 Hook
不须要具备特定签名。咱们能够决定它做为参数须要什么,以及它应该返回什么(若是有的话)
// useVisibleStatus 是一个自定义的钩子,咱们在函数中调用的useEffect
function useVisibleStatus(isShow) {
const [ visible, setVisible ] = useState(isShow);
useEffect(() => {
setVisible(isShow);
}, [ isShow ]);
return visible;
};
function Hooks(props) {
const [ count ] = useState(props.count);
const visible = useVisibleStatus(props.visible);
return (
<div className=""> <h2>{count}</h2> <button onClick={() => setCount(count + 1)}>点击</button> <h2>{`${visible} ${props.count}`}</h2> </div>
);
}
复制代码
咱们也能够将一些复杂或者重复的逻辑提取提取到自定义的 Hook
函数中,从而简化咱们的代码。其实自定义 hook
和函数组件没有多大区别。
当 useState
复杂的状态逻辑涉及多个子值或下一个状态取决于前一个状态时,一般 useReducer
更可取。useReduce
还可让您优化触发深度更新的组件的性能
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter({initialState}) {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<> Count: {state.count} <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ); } 复制代码
咱们看看 useReducer
具体的实现(自定义一个 useReducer
钩子):
function useReducer(reducer, initialState) {
const [state, setState] = useState(initialState);
function dispatch(action) {
const nextState = reducer(state, action);
setState(nextState);
}
return [state, dispatch];
}
复制代码
能够经过 useImperativeHandle
,给ref上绑定一些自定的事件,前提是咱们必须联合 forwardRef
一块儿使用,注意全部的事件都是绑定在 ref
的 current
属性上。 看下面的例子
// hook.js
function Hooks(props, ref) {
const [ count, setCount ] = useState(props.count);
useImperativeHandle(ref, () => ({
// 自定义一些事件
click: () => {
setCount(count + 1);
},
}));
return (
<div className=""> <h2>{count}</h2> <button onClick={() => setCount(count + 1)}>点击</button> </div>
);
};
export default React.forwardRef(Hooks);
// Application.js
export default class App extends PureComponent {
componentDidMount() {
this.ref = React.createRef();
}
return (
<div onClick={() => this.ref.current.click()} > // ... <Hooks ref={this.ref} count={this.state.count} visible={this.state.visible}/> // ... </div> ); } 复制代码
或者
function FancyInput(props, ref) {
// 获取真是DOM节点
const inputRef = React.useRef();
useImperativeHandle(ref, () => ({
// 自定义一些事件
focus: () => {
// 在DOM节点执行一些操做均可以
inputRef.current.focus();
}
}));
return <input ref={inputRef} />; } FancyInput = React.forwardRef(FancyInput); 复制代码
useRef
返回一个可变的ref对象,其 current
属性值为初始化传递的参数(initialValue)。返回的对象将持续整个组件的生命周期。和类组件中的实例属性很像
const ref = usRef(20);
console.log(ref.current) // 20
// 能够从新赋值
ref.current = 200;
复制代码
固然最多见的就是访问一个元素节点
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
复制代码
使用的场景:函数组件中,咱们定义了一些方法,可是咱们并不但愿每次组件更新的时候都从新执行一次个函数,那么这个时候咱们就可使用 useMemo
。有个地方须要注意点那就是,useMemo
是在 useLayoutEffect
以前执行,这和类组件中的 componentWillMount
和 componentWillUpdate
相似。可是若是 useMemo
所接收的条件是 props
中的相关属性的话,那么咱们将 useMemo
看成类组件的 componentWillReceiveProps(nextProps)
使用,能够查看的咱们demo
// 组件初始化的时候会调用 `Func`,相似 `componentWillMount``
// 当数组中的元素的值发生改变,那么就会调用 `Func`,这个条件 a 和 b 有一个发生变化的时候 就会触发 `useMemo`
useMemo(() => Func(a, b), [a, b]);
复制代码
在看这个带返回值的
function Hooks(props) {
const [ count, setCount ] = useState(props.count);
useLayoutEffect(() => {
console.log('useLayoutEffect 后执行');
setCount(props.count);
}, [ props.count ]);
const dom = useMemo(() => {
console.log('useMemo 优先执行');
return <h2>{count * 10}</h2>;
}, [count]);
return (
<div className=""> <h2>{count}</h2> <button onClick={() => setCount(count + 1)}>点击</button> {dom} </div>
);
}
复制代码
注意:传入 useMemo
的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操做,诸如反作用这类的操做属于 useEffec
的适用范畴,而不是 useMemo
useCallback
的使用和 useMemo
是同样的,且 useCallback(fn, deps)
至关于 useMemo(() => fn, deps)
。
这是个人demo
const value = useContext(MyContext);
复制代码
接收一个 context
对象(React.createContext
的返回值)并返回该 context
的当前值。当前的 context
值由上层组件中距离当前组件最近的 <MyContext.Provider>
的 value prop
决定。
当组件上层最近的 <MyContext.Provider>
更新时,该 Hook
会触发重渲染,并使用最新传递给 MyContext provider
的 context value
值。
调用了 useContext
的组件总会在 context
值变化时从新渲染
const Thems = {
light: {
color: '#f90',
},
dack: {
color: '#222',
}
}
const ThemsContext = React.createContext({
them: Thems.light,
toggleTheme: () => {},
})
class Detail extends PureComponent {
state = {
context: {
them: Thems.light.color,
toggleTheme: this.handle,
}
}
handle = () => {
const { context: { them } } = this.state;
let color = Thems.light.color;
if (them === Thems.light.color) {
color = Thems.dack.color
}
this.setState({
context: {
...this.state.context,
them: color,
},
});
}
render() {
return (
<ThemsContext.Provider value={this.state.context}> <Hooks/> </ThemsContext.Provider> ); } } function Hooks(props, ref) { const themContext = React.useContext(ThemsContext); return ( <div style={{ background: themContext.them }} onClick={() => { themContext.toggleTheme() }} > 1234567890 </div> ); } 复制代码
上面的这个 demo 中,当 ThemsContext.Provider
的 value props
发生变化时 Hooks
组件就会发生从新渲染。因此说这个时候咱们的目的就已经达到了。