目前 ,Hooks 应该是 React 中最火的概念了,在阅读这篇文章之前,希望你已经了解了基本的 Hooks 是什么?
下面就介绍一下简单的使用场景
useState
useState是react自带的一个hook函数,它的作用就是用来声明状态变量。 useState这个函数接收的参数是我们的状态初始值(initial state), 它返回了一个数组,这个数组的第[0]项是当前当前的状态值, 第[1]项是可以改变状态值的方法函数。
import React, { useState} from 'react';
function Example() {
//useState的用法,分别是声明、读取、使用(修改)
const [count, setCount] = useState(0) //数组结构
const [age, setAge] = useState(18)
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
return (
);
}
export default Example;
复制代码
基于 useState 的用法,我们尝试着自己实现一个 useState
var _state; // 把 state 存储在外面
function useState(initialValue) {
_state = _state | initialValue; // 如果没有 _state,说明是第一次执行,把 initialValue 复制给它
function setState(newState) {
_state = newState;
render();
}
return [_state, setState];
}
复制代码
useEffect
使用useEffect,可以直接在函数组件内处理生命周期事件。 如果你熟悉 React class 的生命周期函数, 你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合
useEffect 使用需要注意的地方
import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
function useEffectTest() {
useEffect(() => {
// 默认情况下,每次渲染后都会调用该函数
console.log('render!');
// 如果要实现 componentWillUnmount,
// 在末尾处返回一个函数
// React 在该函数组件卸载前调用该方法
// 其命名为 cleanup 是为了表明此函数的目的,
// 但其实也可以返回一个箭头函数或者给起一个别的名字。
return function cleanup () {
console.log('unmounting...');
}
})
return "useEffectTest";
}
复制代码
阻止每次重新渲染都会执行 useEffect 如果希望 effect 较少运行,可以提供第二个参数 - 值数组。 将它们视为该effect的依赖关系。 如果其中一个依赖项自上次更改后,effect将再次运行。
const [value, setValue] = useState(0);
useEffect(() => {
// 仅在 value 更改时更新
console.log(value);
}, [value])
复制代码
useContext
useContext 可以实现共享状态最大的改变是可以在使用 Counter 的时候不必在包裹 Children 了,比方说我们先创建一个上下文, 这个上下文里头有一个名为 count 的 state,以及一个修改 count 的方法 setCount
创建上下文
import React, { createContext, useState } from 'react';
import { Counter } from './Counter'
export const CountContext = createContext()
function Example2() {
const [count, setCount] = useState(0)
return (
)
}
export default Example2;
复制代码
使用上下文
import React, { useContext} from 'react';
import {CountContext} from './Example2'
export function Counter() {
const {count,setCount} = useContext(CountContext) //一句话就可以得到count
return (
{count}
)
}
复制代码
useReducer
Redux 的核心概念是,组件发出 action 与状态管理器通信。状态管理器收到 action 以后,使用 Reducer 函数算出新的状态
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'add':
return { count: state.count + 1 };
case 'sub':
return { count: state.count - 1 };
default:
return state
}
}
function Example2() {
const initialState = { count: 0 };
const [state, dispatch] = useReducer(reducer,initialState)
return (
You clicked {state.count} times
)
}
export default Example2;
复制代码
useMemo
React 16.8.0 中发布useMemo()
React.memo()是判断一个函数组件的渲染是否重复执行
useMemo()是定义一段函数逻辑是否重复执行
useMemo(() => fn, inputs)跟useCallback(fn, inputs)效果一样
const increase = useMemo(() => {
if(value > 2) return value + 1;
}, [value]);
复制代码
useRef
useRef的作用: 获取DOM元素的节点 获取子组件的实例 渲染周期之间共享数据的存储(state不能存储跨渲染周期的数据,因为state的保存会触发组件重渲染)
import React, { useEffect, useRef } from 'react';
function App() {
const h1Ref = useRef();
useEffect(() => {
console.log('useRef')
console.log(h1Ref.current)
}, [])
return Hello World!
}
export default App;
复制代码
useCallback
创建一个回调方法的缓存,可以让我们传入子节点作为props的时候,可以让其没有变化,避免没必要的渲染。
根据输入的inputs,也就是一个数组,内部的内容是否又变回,决定是返回存储的老方法,还是返回新的方法并记录。
import React, { useState, useCallback, useEffect } from 'react';
const set = new Set();
function Callback() {
const [count, setCount] = useState(1);
const [val, setVal] = useState('');
const callback = useCallback(() => {
console.log(count);
}, [count]);
set.add(callback);
return
{count}
{set.size}
setVal(event.target.value)}/>
;
}
export default function Parent() {
const [count, setCount] = useState(1);
const [val, setVal] = useState('');
const callback = useCallback(() => {
return count;
}, [count]);
return
{count}
setVal(event.target.value)}/>
;
}
function Child({ callback }) {
const [count, setCount] = useState(() => callback());
useEffect(() => {
setCount(callback());
}, [callback]);
return
{count}
}
复制代码
useImperativeMethods
接受一个ref作为参数,内部其实就是一个useLayoutEffect的调用。 主要就是在外部传入的ref上挂载内容 ,实现类似ref挂载到ClassComponent上的效果
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeMethods(ref, () : ({
focus: () : {
inputRef.current.focus();
}
}));
return ;
}
FancyInput = forwardRef(FancyInput);
复制代码
useSelector
useSelector用于从Redux存储的state中提取值并订阅该state,这基本上类似于在hooks中实现的mapStateToProps函数
首先,不再提供ownProps API,并且应该使用useCallback或useMemo来通过自定义逻辑获取它们
其次,useSelector()第二个参数也是依赖数组,跟useEffect一样。如果不提供第二个参数每次组件更新时都会重新计算;如果提供了依赖数组,只有依赖数组对应的值变更了之后,才会触发重新计算
除此之外,redux 以前的性能优化逻辑同样保留了下来,如果当前的props跟老的props相同,则组件将不会重新渲染。
由于React redux中使用的批处理更新的逻辑,导致同一组件中的多个useSelector()重新计算出state,只会让组件重新渲染一次。因此,我们可以自由的在组件中useSelector(),而不用担心重复渲染的情况
在下面的例子中,我们可以将单个useSelector()分成两个独立的(一个读取title,另一个读取content)useSelector()他们在性能和渲染数量方面完全相同
import React from 'react';
import { useSelector } from 'react-redux';
const Component = props => {
const { title, content } = useSelector(state => ({
title: state.title,
content: state.content
}));
return {content};
复制代码
useDispatch
除了读取store中的state,还要能dispatch actions来更新store中的state,useDispatch就是这样一个API
只要组件需要触发redux action,那么这个钩子就是你需要的。不幸的是,mapDispatchToProps 被废弃掉了 所以每当你想要dispatch action时,你需要使用dispatch(actionCreator())来调用它的action creator
如果我们初次使用这种方式,会显得有点不太习惯,因为以前都是通过connect HOC来调用被包装成prop的dispatch函数
但hooks的方式会为代码带来更多的清晰度。遗憾的是,如果我们想要在事件处理函数里面dispatch actions,必须创建一个匿名函数, 如:() => dispatch(actionCreator)。由于匿名函数的性质,这将在每次重新渲染时获得新的引用
如果将这个匿名函数作为props传递给子组件组件,那么子组件将每次都重新渲染。为了优化性能,必须使该函数具有相同的引用, 解决方案是在useCallback中创建这个匿名函数。
import { useDispatch } from 'react-redux'
import { logout } from '../store/reducer'
const dispatch = useDispatch()
//登出
const handleLogout = useCallback(() => {
dispatch(logout())
}, [dispatch])