hooks

174 阅读3分钟

为什么是hooks

为什么引入Hook

钩子

useState

官网-useState

  • 用于定义组件的state,对标到类组件中的this.state的功能
function Com() {
    const [state, setState] = useState(0) // 0是默认值
}
useEffect

官网-useEffect

  • 通过依赖触发的钩子函数,常用于模拟组件的生命周期:componentDidMountcomponentDidUpdatecomponentWillUnmount
useEffect(
    ()=>{
        console.info("生命周期1")
	const timer = setTimeout(()=>{},1000)
	return ()=>{
            console.info("生命周期2")
            clearTimerout(timer)
        }
    },
    [n]
)

上面例子中

  • 组件第一次加载的时候,触发了生命周期1,触发的是componentDidMount
  • 当依赖n发生变化的时候,触发了生命周期1,触发的是componentDidUpdate
  • 当组件卸载时,触发了生命周期2,触发的是componentWillUnmount
useLayoutEffect

官网-useLayoutEffect

  • DOM更新的同步钩子,用法和useEffect类似,只是执行时间不同
  • useEffect:属于异步执行,并不会等待DOM真正渲染后执行
  • useLayoutEffect:会真正渲染后才触发,可以获取更新后的state
useReducer

官网-useReducer

  • 类似于Redux思想的实现,单其并不足以替代Redux,可以理解成一个组件内部的redux
  • 不是持久化存储,会随着组件被销毁而销毁
  • 属于组件内部,组件之间无法共享数据

// 初始值
const initialState = {count: 0}; 
// 数据变更方法
function reducer(state, action) {
    const { payload } = action;
    switch (action.type) {
        case 'increment':
            return {count: state.count + 1};
        case 'decrement':
            return {count: state.count - 1};
        default: throw new Error();
     }
}

// 定义
const [state, dispatch] = useReducer(reducer, initState);

// 分发
dispatch({type: 'increment', payload: "传递什么信息呢?"})}
useContext

官网-useContext

  • 提供context对象
var defaultValue = {}
var context = React.useContext(defaultValue);

<context.Provider>
    <context.Consumer>
    
    <context.Consumber>
</context.Provider>
useCallback

官网-useCallback

  • 缓存回调函数,避免传入的回调每次都是新的函数实例而导致依赖组件重新渲染
  • 返回值:memoized函数
const memoizedCallback = useCallback(() => {
    // 这里做什么
}, [a])

当a变化的时候,才会重新生成新的函数

useMemo

官网-useMemo

  • 返回值: memoized 值
  • 用于缓存传入的props,避免依赖的组件每次都重新渲染.可以实现值的缓存
// 没有使用useMemo
const data = {
    name,
}

上面看着好像没有什么问题,但是你要知道,每次函数钩子,执行update的时候,data都会被赋值一个新的对象,data就不是原来的对象了。这个就会不停的触发更新

// 使用useMemo
const data = useMemo(function useMemoCallback(){
    return {
        name,
    }
}, [name])

上面的例子,如果name变化,useMemoCallback函数,会执行,并把返回值赋值给data。当name没有变化的情况下,data的值始终是一个对象地址

useMemo模拟memo

如题,如何模拟呢?如果你不知道memo的作用,先看下面的memo讲解

useMemo(() => {
    return <Card1 name={name}/>
}, [name])

如果name没有变化,useMemo返回的卡片组件,就不会重复render,实现了一个PureComponent的方法

useRef

官网-useRef

  • 返回值:一个可变的ref对象
  • 用来存储一个常量

方法一:保存常量

var refValue = useRef(null)
console.info(refValue.current)
refValue.current = 112;

方法二:引用dom

function Com() {
    var inputRef = React.useRef();
    return <input ref={inputRef} />
}

React-useimperativehandle

通常结合forwordRef使用

function Func(props, ref) {
    const inputRef = React.useRef();
    useimperativehandle(ref, () => {
        // 扩展了Func这个函数组件的ref方法,新增了一个focus
        focus: inputRef.current.focus();
    })
    return <input ref={inputRef}/>
}
const MyInput = React.forwardRef(Func)

未来使用MyInput时,可以通过myinputRef.current.focus来触发Func组件中的input框的focues事件

其他

React顶层API

Q & A

useState没有回调函数,如何处理
  • 在class组件中this.setState方法,接收第二个回调参数,在re-render后执行,但是这样的情况下,更推荐comonentDiUpdate中实现回调函数的执行
class Com extends React.Component {
    state = {
        count: 1,
    }
    componentDidMount() {
        this.setState({ count: 3 }, () => {
            console.info(this.state.count)
        })
    }
    
    componentDidUpdate() {
        console.info(this.state.count)
    }
    render() {
        return null;
    }
}
  • 函数组件可以使用useEffectuseLayoutEffect模拟实现componentDidUpdate
function Com() {
    const [count, updateCount] = React.useState(0);
    useEffect(() => {
        updateCount(1)
    }, [])
    useEffect(() => {
        console.info(count)
    }, [count])
}