持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情
什么是useReducer
用于状态管理的api,useState是使用useReducer构建的。
区别:useReducer管理公共状态,useState管理私有内容状态
const [state,dispatch] =useReducer(reducer,initState,init惰性初始化)
一共有三个参数,一个是reducer函数,它有两个参数(state,action),返回新的state。
initState是初始化state的状态
init是惰性初始化
情况1.
当该公共状态为简单数据类型时
let initState = 0
定义reducer函数
function reducer(state,action){
return state;
}
定义UI组件:
点击increase/decrease按钮,派发action指令
function App(){
const [state,dispatch] = useReducer(reducer,initState)
return (
<div>
<p>count: {state}</p>
<button onClick={()=>dispatch("increase")}>count++</button>
<button onClick={()=>dispatch("decrease")}>count--</button>
</div>
)
}
在reducer中改变state,这里的state默认为useReducer第二个参数initState
switch (action) {
case "increase":
return state+1
case "decrease":
return state-1
default:
break;
}
情况2.
初始数据是个对象
let initState = {count:0}
定义reducer
function reducer(state,action){
return state;
}
定义UI组件
function App(){
const [state,dispatch] = useReducer(reducer,initState)
return (
<div>
<p>count: {state.count}</p>
<button onClick={()=>dispatch({type:"increase"})}>count++</button>
<button onClick={()=>dispatch({type:"decrease"})}>count--</button>
<button onClick={()=>dispatch({type:"reset",payload:initState})}>count惰性初始化</button>
</div>
)
}
派发action对象
switch (action.type) {
case "increase":
return {count:state.count+1} //返回一个对象去覆盖
case "decrease":
return {count:state.count-1}
case "reset":
return {count:action.payload.count+5} //在初始数据的基础上+5
default:
break;
}
注意,不可以使用return state.count = state.count+1的方式更改count数据
因为初始对象拥有一个地址,只改变里面的count是不能做到重新渲染该对象的,只能声明新的对象产生新的地址才能更改对象里的数据。这是钩子函数内部优化的机制。
使用useContext
在类组件中传统的父子间传值方式就是使用props,通过props接收到父组件传递过来的数据
从App父组件开始传递数据到子组件中,传统的props方法我们就不在此赘述。
直接使用useContext钩子函数
通过createContext方法返回Provider,Consumer两个组件
使用方法与context上下文相同,将需要传递数据的组件用Provider包裹起来,在接收数据的子组件中使用Consumer包裹并用对象返回值的形式接收。
单一数据
function App(){
const [theme,setTheme] = useState("red")
return(
<div>
<p>theme color : {theme}</p>
<button onClick={()=>{setTheme("green")}}>改变主题颜色</button>
//使用value传递数据
<Provider value={theme}>
<Child1 />
<Child2 />
</Provider>
</div>
)
}
Child1子组件:
class Child1 extends Component{
render(){
return(
//使用Consumer组件包裹
<Consumer>
//对象形式
{
//接收传递过来的value,拿到theme
(theme)=>{
//以返回值的形式,返回出带有theme的UI组件
return (
<div >
<p>theme1 color :{theme}</p> //可以接收到了
<Child3 />
<Child4 />
</div>
)
}
}
</Consumer>
)
}
}
Child3子组件:
class Child3 extends Component{
render(){
return(
//使用consumer包裹
<Consumer>
{
//接收theme
(theme)=>{
return (
<div >
<p>theme1-3 color :{theme}</p>
</div>
)
}
}
</Consumer>
)
}
}
这是使用provider传递一个值的情况,如果使用provider传递多个值又是怎样呢。
多个数据
那就用createContext创建两个provider,consumer
const ColorContext = createContext("grey")
const FontContext = createContext("粗体")
使用方法:
同样,在想要传递值的组件外包裹这两个Provider组件
<ColorContext.Provider value={theme}> //传递theme
<FontContext.Provider value={font}> //传递字体
<Child1 />
<Child2 />
</FontContext.Provider>
</ColorContext.Provider>
在两个子组件中使用Consumer接收数据:
以Child1为例
class Child1 extends Component {
render() {
return (
<ColorContext.Consumer> //接收theme数据
{//要包裹住,因为里面在执行函数
(theme) => {
return (
<FontContext.Consumer> //接收font数据
{ //要包裹住,因为里面在执行函数
(font) => {
return(
<div >
<p>theme1 color :{theme}</p>
<p>theme1 font :{font}</p>
<Child3 />
<Child4 />
</div>
)
}
}
</FontContext.Consumer>
)
}
}
</ColorContext.Consumer>
)
}
}
结合react-context
useContext使用方法可以说是和context使用方法基本相同,我之前也有些过context上下文的使用,大家可以一看,奉上链接:react-context上下文