useContext
在理解useContext之前 肯定会想到最简单的也最麻烦的props传值
useContext 和 props的共同与不同点
共同点:
1、如果需要在组件之间共享状态,可以使用useContext(),也可以使用props,props和useContext都可以实现组件之间的共享状态
不同点:
1、props存在的问题是props传输的值需要层层传输,即下层组件想要共享状态,必须是上层组件一个一个传下来的,不能跳级传。但是useContext,无论有多少层子组件,他们都可以直接获取最上层定义的值,所以下层组件要共享状态无需通过上传组件的传输。
他们的使用也要根据情况来区分,如果在只有一层或两层的少量层数组件通信时,采用props是比较简单的方法,而当有多层组件,且有写组件用不到这个状态时,写useContext是比较好的方法。可以免去一层层写props的麻烦。
说得再多还不如用代码演示一下让人理解更深刻。
props传的例子就不写了,这次着重展示useContext如何传递
//这个被称作父组件其实不太准确,因为其可能包了很多个子组件 子组件又包了很多个子孙组件 所以应该叫做最外层组件
const Father = () => {
//首先,在最外层组件(App)中创建 const AppContext = React.createContext({})
const AppContext = React.createContext({});// 表示初始值是个对象
return (
//下层组件如果内部需要引用这个状态的话,需要使用const {userName} = userContext(AppContext),这样就可以简单的得到上层组件定义的状态(值)
//此时最外层组件已经将username这个值传入成功了
<AppContext.Provider value={
{username:'我是最外层组件传来的数据'}}>
<div>我是最外层组件组件</div>
<Child1></Child1>
<Child2></Child2>
</AppContext.Provider>
)
}
export default Father;
于是在Child1和Child2组件中获得userName的方式如下
const Child1 = () => {
//useContext()钩子函数用来引入Context对象,从中获取username属性
const {username} = useContext(AppContext)
return (
<>
<div>{username}</div>
</>
)
}
const Child2 = () => {
const {username} = useContext(AppContext)
return (
<>
<div>{username}</div>
<Child3></Child3>
</>
)
}
其中Child2中还嵌套了子组件Child3 如果是props传递 此时就要在子组件Child3上加入props 会比较麻烦,但是使用useContext就会十分方便 和Child1和Child2组件获得username的方式一样
const Child3 = () => {
//useContext()钩子函数用来引入Context对象,从中获取username属性
const {username} = useContext(AppContext)
return (
<>
<div>{username}</div>
</>
)
}
总结: Child3子组件,或者更下层组件想要引用这个状态的话,之间加上 const {userName} = useContex(AppContex)即可获得。不需要父组件的传输 ,比props方便不少。
useReducer
首先,useReducer 是 useState 的替代方案,或是优化方案
const [state, dispatch] = useReducer(reducer, initialArg);
上面就是useReducer的定义方式
useReducer的参数有两个:
1、reducer函数
2、是初始化的state。返回值为最新的state和dispatch函数(用来触发reducer函数,计算对应的state)
为了更好的理解useReducer 我把useState转成useReducer
首先先用useState写一个demo 我从其他地方copy来的哈哈哈哈
//useState 计数器
function Counter({initialCount}) {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
);
}
如上所示 这是一个简单的加减复位的一个demo,接下来我们将它改写成useReducer
const initialState = {count: 0};
const reducer = (state, action) => {
switch (action.try) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
};
//useReducer 计数器
const Counter ({initialCount}) => {
const [count, setCount] = useReducer(reducer,initialCount)
return (
Count: {count}
<button onClick={() => setCount({try: 'increment'})}>+</button>
<button onClick={() => setCount({try: 'decrement'})}>-</button>
)
}
如上就是useReducer的写法,但是这个例子中 state只有一个 当state比较多的时候,采取es6的解构赋值就行
const reducer = (state, action) => {
switch (action.try) {
case 'increment':
//此时就是es6的解构赋值的应用
return {...state, count: state.count + 1};
case 'decrement':
return {...state, count: state.count - 1};
default:
throw new Error();
}
};
其实这里还涉及到了不可变数据,此时的state对象必须是immutable,具体的不可变数据我再写一章。
终于终于!!!! 王炸来了!!!!!
useState + useContext 结合实现类似redux的数据管理效果
在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。
首先 创建一个context对象
Context.tsx
import { createContext } from "react";
//定义了一个ReducerContext,这样任何组件需要使用只需要引入就可以了
export const ReducerContext = createContext(null);
index.tsx
import { useReducer } from 'react';
import ChildComponent from './ChildComponent';
import { ReducerContext } from './Context';
//定义useReducer的两个参数 addData 和 addCount
const addData = {
count: 0,
}
const addCount = (state, action) => {
switch (action.type) {
case 'increment':
return {
...state,
count: state.count + 1,
}
case 'decrement':
return {
...state,
count: state.count - 1,
}
default:
throw new Error();
}
}
const index = () => {
const [state, dispatch] = useReducer(addCount, addData);
return (
<div>
<ReducerContext.Provider value={state,dispatch}>
<ChildComponent />
</ReducerContext.Provider>
</div>
)
}
export default index;
ChildComponent.tsx
import { memo } from "react";
import Child1 from './Child1';
import Child2 from './Child1';
const ChildComponent = () => {
const state = useContext(ReducerContext)
return (
<>
Count:{state.count}
<Child1 />
<Child1 />
</>
)
}
export default memo(ChildComponent);
Child1.tsx
import { useContext } from "react";
import { ReducerContext } from "./Context";
const Child1 = () => {
const dispatch = useContext(ReducerContext)
return (
<>
<button onClick={()=>dispatch({type:'increment'})}>+</button>
<button onClick={()=>dispatch({type:'decrement'})}>-</button>
</>
)
}
export default Child1;
以上就是一套完整的useReducer和useContext结合使用的基础思路吧 有错误及时帮忙指正哈
看起来有点复杂累赘 但是是分情况使用的:
1、页面state很简单,可以直接使用useState,比较简洁
2、面state比较复杂(state是一个对象或者state非常多散落在各处)请使用userReducer,方便统一管理
3、页面组件层级比较深,并且需要子组件触发state的变化,可以考虑useReducer + useContext