Open shenxuxiang opened 5 years ago
Hook是React 16.8中的新增功能。它们允许您在不编写类的情况下使用状态和其他React功能。HOOKS只能在函数组件中使用
React.memo 是一个高阶的组件。它类似于React.PureComponent 也就是说如果组件的 props 没有改变,是不会被重新渲染的。
React.memo
React.PureComponent
props
function Foo (props) { ... } export default React.memo(Foo)
类似于类组件中的state,不同的是 useState 接受一个任意类型的值 string, array, object, bool... 作为参数并返回一个数组,且 useState 只会在组件初始化的时候执行。
useState
string, array, object, bool...
// 初始化的时候,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> ); };
这个类似类组件中的 componentDidMount 和 componentDidUpdate。每次当函数组件挂载成功或者重新渲染完成后都会调用 useEffect 。 之所以说类似,是因为 useEffect 不完全同类组件中的 componentDidMount 和 componentDidUpdate生命周期函数一样,useEffect 有延迟,在父组件didMount或didUpdate后,但在任何新渲染之前触发。useEffect可以在组件中使用多次。 useEffect 还可以返回一个函数,并在组件即将销毁时调用这个返回函数,没错,就是和类组件的 componentWillUnmount 一样。
componentDidMount
componentDidUpdate
useEffect
componentWillUnmount
function Hooks(props) { const [ age, setAge ] = useState(20); // 当组件挂载成功后调用下面的函数 // 当props.visible 改变了,那么会在组件重新渲染完成以后调用下面的函数 // 当调用setAge,age发生改变,那么会在组件重新渲染完成以后调用下面的函数 useEffect(() => { console.log(props); // 会在组件willUnmount时候调用 return () => {...} }); return ( <div className=""> <p>我的年龄是{age}岁</p> <button onClick={() => setAge(age + 1)}>点击</button> <p>{`${visible}`}</p> </div> ); };
类似于类组件中的 componentDidUpdate(prevProps, prevState) ,这个生命周期,那么如何使用呢?
componentDidUpdate(prevProps, prevState)
function Hooks(props) { const [ age, setAge ] = useState(20); const [ visible, setVisible ] = useState(props.visible); // 当函数调用时发现props.visible发生了变化,类似于类组件中的componentDidUpdate(prevProps, prevState) // 当prevProps.visible !== this.props.visible 那么就会执行useEffect的函数体 useEffect(() => { console.log('visible is changed'); setVisible(props.visible); }, [ props.visible ]); // 当函数调用时发现age发生了变化,类似于类组件中的componentDidUpdate(prevProps, prevState) // 当prevState.age !== this.state.age 那么就会执行useEffect的函数体 useEffect(() => { console.log(age, 1111); }, [ age ]); return ( <div className=""> <p>我的年龄是{age}岁</p> <button onClick={() => setAge(age + 1)}>点击</button> <p>{`${visible}`}</p> </div> ); };
如果参数中有多个元素 [ age, props.visible ] ,那么元素的关系是 age && props.visible ,通过比较后只要有一个元素发生变化,useEffect 就会执行。如果参数是一个空数组 [] ,那么这个时候 useEffect 就和类组件中的 componentDidMount 一样,只在组件刚刚挂载的时候调用一次。 useEffect 函数中return的函数,不受第二个参数的影响,仍在组件 WillUnmount 的时候调用。 不要在循环条件或嵌套函数中调用Hook。相反,始终在React函数的顶层使用Hooks。通过遵循此规则,您可以确保每次组件呈现时都以相同的顺序调用Hook。这就是React允许多个useState和useEffect调用之间正确保留Hook状态的原因。
[ age, props.visible ]
age && props.visible
[]
WillUnmount
和 useEffect 使用原理相同,但是唯一的区别在于 useLayoutEffect 不会延迟触发,和类组件的 componentDidMount 和 componentDidUpdate 这两个生命周期函数是同步的,其他没有区别。
useLayoutEffect
自定义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更可取。useReducer还可以让您优化触发深度更新的组件的性能
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 具体的实现(和自定义hook没有差异):
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 属性上。 看下面的例子
useImperativeHandle
forwardRef
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 = useRef(); useImperativeHandle(ref, () => ({ // 自定义一些事件 focus: () => { // 在DOM节点执行一些操作都可以 inputRef.current.focus(); } })); return <input ref={inputRef} />; } FancyInput = forwardRef(FancyInput);
useRef 返回一个可变的ref对象,其 .current 属性值为初始化传递的参数(initialValue)。返回的对象将持续整个组件的生命周期。和class组件中的实例属性很像
useRef
.current
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 类似。可以查看的我们demo
componentWillMount
componentWillUpdate
// 组件初始化的时候会调用Func,类似 componentWillMount // 当数组中的元素的值发生改变,那么就会调用Func,这个条件 `a && b` 的关系 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> ); };
useCallback 的使用和 useMemo 是一样的,且 useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。
useCallback
useMemo
useCallback(fn, deps)
useMemo(() => fn, deps)
这是我的demo
HOOKS
Hook是React 16.8中的新增功能。它们允许您在不编写类的情况下使用状态和其他React功能。HOOKS只能在函数组件中使用
memo
React.memo
是一个高阶的组件。它类似于React.PureComponent
也就是说如果组件的props
没有改变,是不会被重新渲染的。useState
类似于类组件中的state,不同的是
useState
接受一个任意类型的值string, array, object, bool...
作为参数并返回一个数组,且useState
只会在组件初始化的时候执行。数组中的第一个元素是状态值,组件在运行过程中会保留这个状态值,类似于this.state 数组中的第二个元素是改变这个状体值的函数,类似于this.setState()
useEffect
这个类似类组件中的
componentDidMount
和componentDidUpdate
。每次当函数组件挂载成功或者重新渲染完成后都会调用useEffect
。 之所以说类似,是因为 useEffect 不完全同类组件中的componentDidMount
和componentDidUpdate
生命周期函数一样,useEffect
有延迟,在父组件didMount或didUpdate后,但在任何新渲染之前触发。useEffect可以在组件中使用多次。useEffect
还可以返回一个函数,并在组件即将销毁时调用这个返回函数,没错,就是和类组件的componentWillUnmount
一样。useEffect也可以接收一个数组作为第二个参数
类似于类组件中的
componentDidUpdate(prevProps, prevState)
,这个生命周期,那么如何使用呢?如果参数中有多个元素
[ age, props.visible ]
,那么元素的关系是age && props.visible
,通过比较后只要有一个元素发生变化,useEffect
就会执行。如果参数是一个空数组[]
,那么这个时候useEffect
就和类组件中的componentDidMount
一样,只在组件刚刚挂载的时候调用一次。useEffect
函数中return的函数,不受第二个参数的影响,仍在组件WillUnmount
的时候调用。 不要在循环条件或嵌套函数中调用Hook。相反,始终在React函数的顶层使用Hooks。通过遵循此规则,您可以确保每次组件呈现时都以相同的顺序调用Hook。这就是React允许多个useState和useEffect调用之间正确保留Hook状态的原因。useLayoutEffect
和
useEffect
使用原理相同,但是唯一的区别在于useLayoutEffect
不会延迟触发,和类组件的componentDidMount
和componentDidUpdate
这两个生命周期函数是同步的,其他没有区别。customize hooks
自定义Hook是一个JavaScript函数,其名称以“use” 开头,可以调用其他Hook。构建自己的Hook可以将组件逻辑提取到可重用的函数中 ,确保只在自定义Hook的顶层无条件地调用其他Hook。与React组件不同,自定义Hook不需要具有特定签名。我们可以决定它作为参数需要什么,以及它应该返回什么(如果有的话)
我们也可以将一些复杂或者重复的逻辑提取提取到自定义的hook函数中,从而简化我们的代码。其实自定义hook和函数组件没有多大区别。
useReducer
当
useState
复杂的状态逻辑涉及多个子值或下一个状态取决于前一个状态时,通常useReducer更可取。useReducer还可以让您优化触发深度更新的组件的性能我们看看 useReducer 具体的实现(和自定义hook没有差异):
useImperativeHandle
可以通过
useImperativeHandle
,给ref上绑定一些自定的事件,前提是我们必须使用forwardRef
,注意所有的事件都是绑定在ref的current
属性上。 看下面的例子或者
useRef
useRef
返回一个可变的ref对象,其.current
属性值为初始化传递的参数(initialValue)。返回的对象将持续整个组件的生命周期。和class组件中的实例属性很像当然最常见的就是访问一个元素节点
useMemo
使用的场景:函数组件中,我们定义了一些方法,但是我们并不希望每次组件更新的时候都重新执行一次个函数,而是变成有条件的触发,那么这个时候我们就可以使用useMemo。有个地方需要注意点那就是,useMemo是在useLayoutEffect之前执行,这和类组件中的
componentWillMount
和componentWillUpdate
类似。可以查看的我们demo在看这个带返回值的
useCallback
useCallback
的使用和useMemo
是一样的,且useCallback(fn, deps)
相当于useMemo(() => fn, deps)
。这是我的demo