React Hook的基本使用
Hook 是什么? 在中文官网中是这么介绍的:
Hook 是一个特殊的函数,它可以让你“钩入” React 的特性。例如,useState 是允许你在 React 函数组件中添加 state 的 Hook。
什么时候我会用 Hook?
如果你在编写函数组件并意识到需要向其添加一些 state,以前的做法是必须将其它转化为 class。现在你可以在现有的函数组件中使用 Hook。当你想在函数式组件中使用类式组件的生命周期方法时可以使用Hook。
一:useState
在类式组件中我们要在构造函数中声明this.state={count:0}的方式来使用变量count,但在函数式组件中并没有构造函数,所以就可以使用useState Hook来使用变量,使用方式如下:
- 首先需要在react中引入useState Hook;
import React,{useState} from 'react'
- 在函数式组件中初始化,等号左侧中括号中第一个值count为要使用的变量,
const [count,setCount]=useState(10);
- setCount时一个函数,只能调用改函数修改count的值,等号右侧Hook中的参数10为count的初始值;
- 例子中点击按钮时就会调用setCount函数来修改count的值。
<button onClick={()=>setCount(count+2)}>点击+2</button>
二:useRef
useRef Hook 经常用在表单元素中,用来获取对表单元素的引用,从而获取对应表单元素的值。使用方式如下:
- 同样第一步需要从react中引入useRef Hook
import React,{useRef} from 'react'
- 在函数式组件中初始化,null为初始值,可以省略
const input=useRef(null);
- 给表单元素的ref属性与useRef初始化的变量建立引用关系
<input type="text" ref={input} onChange={handleChange} />
- 在处理函数中获取表单元素的值,current属性可以获取到对应的html节点
console.log('ref:',input.current.value);
在这个例子中,因为处理函数监听的是调用函数的节点本身(input元素的onChange事件调用了handleChange处理函数),所以在处理函数中会有一个参数event(例子中用e表示),通过event的target属性也可以拿到事件对应的节点,进一步获取值,react在官网中也提醒了大家尽可能少用ref属性。
三:useEffect
在函数式组件中使用useEffect就可以使用类似于类式组件的生命周期方法,具体使用方式如下:
- useEffect的第一个参数是一个回调函数,第二个参数是一个数组,数组为空时表示改Hook只在组件第一次挂载时使用,类似于生命周期方法componentDidMount()
- 第二个参数数组中是要监听的变量,表示改变量值发生改变时调用第一个回调函数,可以监听多个变量,类似于生命周期方法componentWillUpdate();
- 第一个参数(回调函数)可以返回一个函数,改函数会在组件卸载时执行,类似于生命周期方法componentWillUnmount();
四:useContext
useContext Hook 可以在多层嵌套的组件之前快速直接的进行参数传递,官网中是如下介绍的:
接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。当组件上层最近的 <MyContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。即使祖先使用 React.memo 或 shouldComponentUpdate,也会在组件本身使用 useContext 时重新渲染。
-
使用方式如下:
- 调用React.createContext()方法会生成一个类似于组件的元素,例子中为TaskContext,
- 用生成的TaskContext.Provider包裹子组件,通过value属性给子组件传值
- 子组件通过useContext Hook 来接收上层组件传递来的值,例如:
let task=useContext(TaskContext)
- 变量task中就包含了上层组件传递的值,就可以在本组件中使用。
五: useReducer
useState的另一种选择。接受类型为(state, action) => newState的reducer,并返回与分派方法配对的当前状态。(如果您熟悉Redux,您应该已经知道它是如何工作的).
当你有复杂的状态逻辑,包括多个子值,或者当下一个状态依赖于前一个状态时,useReducer通常比useState更好。useReducer还允许您优化触发深度更新的组件的性能,因为您可以向下传递分派而不是回调.
React保证分派函数标识是稳定的,并且在重新呈现时不会改变。这就是为什么从useEffect或useCallback依赖项列表中省略是安全的。
有两种不同的方法来初始化useReducer状态。您可以根据用例选择其中任何一个。最简单的方法是将初始状态作为第二个参数传递.
还可以惰性地创建初始状态。为此,您可以传递一个init函数作为第三个参数。初始状态将被设置为init(initialArg)。
- useReducer就是阉割版的redux,实现了redux的一些功能,有时候可以当做redux来使用,但缺少了状态共享的功能,常与useContext搭配使用。
- useReducer接受三个参数:
- 第一个为reducer函数,与redux中的reducer使用方法相同
- 第二个为初始的state值
- 第三个参数可选,是一个函数,useReducer会把第二个参数(也就是初始的state值)传给改函数作为参数;
- useReducer的常见使用景为:
- state不是基本数据类型,而是引用类型时
- state的变化很复杂时
- 需要去嵌套很深的子组件中去修改状态时
- 应用很复杂时,把业务和UI分开
基本使用:
import React, { useReducer } from "react";
const initalState = { count: 0 };
const createIncrementAction = (data) => ({ type: "increment", data });
const createDecrementAction = (data) => ({ type: "decrement", data });
function reducer(state, action) {
let { type, data } = action;
let { count } = state;
switch (type) {
case "increment":
return { count: count + data };
case "decrement":
return { count: count - data };
default:
throw new Error();
}
}
export default function UseReducer() {
const [state, dispatch] = useReducer(reducer, initalState);
return (
<div>
<p>Count:{state.count}</p>
<button onClick={() => dispatch(createIncrementAction(2))}>
increment 2
</button>
<button onClick={() => dispatch(createDecrementAction(3))}>
decrement 3
</button>
</div>
);
}
惰性创建初始state:
import { useReducer } from "react";
const init = (initalCount) => ({ count: initalCount });
function reducer(state, action) {
let { type, data, payload } = action;
switch (type) {
case "increment":
return { count: state.count + data };
case "decrement":
return { count: state.count - data };
case "reset":
return init(payload);
default:
throw new Error();
}
}
const createIncrementAction = (data) => ({ type: "increment", data });
const createDecrementAction = (data) => ({ type: "decrement", data });
const createResetAction = (payload) => ({ type: "reset", payload });
export default function Counter({ initialCount = 0 }) {
const [state, dispatch] = useReducer(reducer, initialCount, init);
return (
<>
Count: {state.count}
<button onClick={() => dispatch(createResetAction(initialCount))}>
Reset
</button>
<button onClick={() => dispatch(createDecrementAction(2))}>-2</button>
<button onClick={() => dispatch(createIncrementAction(3))}>+3</button>
</>
);
}