前言:Hook 是 React 16.8 的新增特性,思想更趋近于函数式编程,用函数声明方式代替class声明方式,无需像class声明组件那样写声明周期,写render函数等
一:useContext:
核心功能:解决父子组件之间传值
下面让我们来回顾一下几种常见的传值方式
1.props传值:
数据是通过 props 属性自上而下(由父及子)进行传递的,组件树的逐层传递 props非常的繁琐
import React, { Component } from 'react'
class Father extends Component {
render(){
return(
<Son value='dark' />
)
}
}
function Son(props) {
return (
<div>
< GrandChild value={props.value} />
</div>
);
}
class GrandChild extends Component {
render() {
return <div>{this.props.value}</div>
}
}
export default Father
2.Context 传值:
Context 提供了一个无需为每层组件手动添加 props,就能将值深入传递进组件树,避免通过中间元素层层传递 props
创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。
只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效
import React, { Component } from 'react'
const ThemeContext = React.createContext('light');
class Context extends Component {
render(){
return(
<ThemeContext.Provider value='dark'>
<Toolbar />
</ThemeContext.Provider>
)
}
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends Component {
render() {
return(
<ThemeContext.Consumer>
{(value) => (
<div>{value}</div>
)}
</ThemeContext.Consumer>
)
}
}
export default Context
3.useContext取值
作用: useContext可以很方便去订阅context的改变,读取context的值并渲染在组件上。例如上面的函数式组件中,通过Consumer的形式获取Context的数据,有了useContext可以改写成下面:你仍然需要在上层组件树中使用 <MyContext.Provider> 来为下层组件提供 context。
接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。
const MyContext = React.createContext(defaultValue);
const value = useContext(MyContext); // useContext参数必须是context对象本身
import React , {useContext , createContext } from 'react'
const CountContext = createContext(0)
function UseContext() {
return(
<div>
<CountContext.Provider value={2}>
<Counter />
</CountContext.Provider>
</div>
)
}
function Counter(){
let count = useContext(CountContext);
return(
<h2>{count}</h2>
)
}
export default UseContext
如果你在接触 Hook 前已经对 context API 比较熟悉,那应该可以理解,useContext(MyContext) 相当于 class 组件中的 static contextType = MyContext 或者 <MyContext.Consumer>。
二:userReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);
useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。(如果你熟悉 Redux 的话,就已经知道它如何工作了。)
1.Redux工作原理
Redux 的核心思想之一就是将状态放到唯一的全局对象(一般称为 Store)中,而修改状态则是调用对应的 Reducer 函数去更新 Store 中的状态,大概就像这样:
上图描述的是组件 A 改变 B 和 C 中状态的过程:
- 三个组件挂载时,从 Store 中获取并订阅相应的状态数据并展示(注意是只读的,不能直接修改)
- 用户点击组件 A,触发事件监听函数
- 监听函数中派发(Dispatch)对应的动作(Action),传入 Reducer 函数
- Reducer 函数返回更新后的状态,并以此更新 Store
- 由于组件 B 和 C 订阅了 Store 的状态,所以重新获取更新后的状态并调整 UI
reducer示例
function countReducer(state,action){
switch(action.type){
case 'add':
return state+1
case 'sub':
return state-1
default:
return state
}
}
2.useReducer的使用方法
const [state, dispatch] = useReducer(reducer, initialArg, init)
- 第一个参数 reducer 显然是必须的,它的形式跟 Redux 中的 Reducer 函数完全一致,即 (state, action) => newState。
- 第二个参数 initialArg 就是状态的初始值。
import { useReducer } from "react";
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 UseReducer() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
<div>count:{state.count}</div>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
export default UseReducer
3.第三个参数 init 是一个可选的用于懒初始化(Lazy Initialization)的函数,这样初始 state 将被设置为 init(initialArg)。这也为将来对重置 state 的 action 做处理提供了便利:
import { useReducer } from "react";
function init(initialCount) {
return {
count: initialCount
};
}
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
case 'reset':
return init(0)
default:
throw new Error();
}
}
function UseReducer() {
const [state, dispatch] = useReducer(reducer, 0, init);
return (
<>
<div>count:{state.count}</div>
<button onClick={() => dispatch({type: 'decrement'})}>减</button>
<button onClick={() => dispatch({type: 'increment'})}>加</button>
<button onClick={() => dispatch({type: 'reset'})}>还原</button>
</>
);
}
export default UseReducer
其实就是一个简单的读取值和改变值,其实用useState的话一行代码就能搞定
3.什么时候该用 useReducer
useReducer 和 useState 的使用目的几乎是一样的:定义状态和修改状态的逻辑。useReducer 使用起来较为繁琐,但如果你的状态管理出现了至少一个以下所列举的问题:
- 需要维护的状态本身比较复杂,多个状态之间相互依赖
- 修改状态的过程比较复杂