文章目录
Redux Hooks
Redux中Hooks介绍
在之前的redux开发中,为了让组件和redux结合起来,我们使用了react-redux库中的connect:
但是这种方式必须使用高阶函数结合返回的高阶组件;
并且必须编写:mapStateToProps和 mapDispatchToProps映射的函数, 具体使用方式在前面文章有讲解;
在Redux7.1开始,提供了Hook的方式,在函数组件中再也不需要编写connect以及对应的映射函数了
**
useSelector
的作用是将state映射到组件中**:
参数一: 要求传入一个回调函数, 会将state传递到该回调函数中; 回调函数的返回值要求是一个对象, 在对象编写要使用的数据, 我们可以直接对这个返回的对象进行解构, 拿到我们要使用state中的数据
const{ counter }=useSelector((state)=>{return{counter: state.counter.counter }})
参数二: 可以进行比较来决定是否组件重新渲染;
useSelector默认会比较我们返回的两个对象是否相等;
如何可以比较呢?
- 在useSelector的第二个参数中, 传入react-redux库中的
shallowEqual
函数就可以进行比较import{ shallowEqual }from'react-redux'const{ counter }=useSelector((state)=>({counter: state.counter.counter }), shallowEqual)
也就是我们必须返回两个完全相等的对象才可以不引起重新渲染;
**
useDispatch
非常简单,就是调用useDispatch这个Hook, 就可以直接获取到dispatch函数,之后在组件中直接使用即可**;
const dispatch =useDispatch()
我们还可以通过useStore来获取当前的store对象(了解即可, 不建议直接操作store对象);
Redux中Hooks使用
我们来使用Redux的Hooks在App组件实现一个计数器, 在App的子组件中实现一个修改message的案例:
首先我们先创建一个简单的store
// store/modules/counter.jsimport{ createSlice }from"@reduxjs/toolkit";const counterSlice =createSlice({name:"counter",initialState:{counter:10,message:"Hello World"},reducers:{changeNumberAction(state,{ payload }){
state.counter = state.counter + payload
},changeMessageAction(state,{payload }){
state.message = payload
}}})exportconst{ changeNumberAction, changeMessageAction }= counterSlice.actions
exportdefault counterSlice.reducer
// store/index.jsimport{ configureStore }from"@reduxjs/toolkit";import counterSlice from"./modules/counter"const store =configureStore({reducer:{counter: counterSlice
}})exportdefault store
要使用react-redux库需要导入Provider对App组件进行包裹
import React from"react"import ReactDOM from"react-dom/client"import{ Provider }from"react-redux"import App from"./12_Redux中的Hooks/App"import store from"./12_Redux中的Hooks/store"const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(<Provider store={store}><App/></Provider>)
在组件时使用useSelector和useDispatch实现获取store中的数据和修改store中数据的操作
import React,{ memo }from'react'import{ useDispatch, useSelector }from'react-redux'import{ changeMessageAction, changeNumberAction }from'./store/modules/counter'// 子组件Homeconst Home =memo(()=>{
console.log("Home组件重新渲染")// 通过useSelector获取到store中的数据const{ message }=useSelector((state)=>({message: state.counter.message
}))// useDispatch获取到dispatch函数const dispatch =useDispatch()functionchangeMessage(){dispatch(changeMessageAction("Hello ChenYq"))}return(<div><h2>{message}</h2><button onClick={changeMessage}>修改message</button></div>)})// 根组件Appconst App =memo(()=>{
console.log("App组件重新渲染")// 通过useSelector获取到store中的数据const{ counter }=useSelector((state)=>({counter: state.counter.counter
}))// useDispatch获取到dispatch函数const dispatch =useDispatch()functionchangeNumber(num){dispatch(changeNumberAction(num))}return(<div><h2>当前计数:{counter}</h2><button onClick={()=>changeNumber(1)}>+1</button><button onClick={()=>changeNumber(-1)}>-1</button><Home/></div>)})exportdefault App
现在我们已经在组件中使用并且修改了了store中的数据, 但是现在还有一个小问题(性能优化)
当App组件中修改了counter时, App组件会重新渲染这个是没问题的; 但是Home组件中使用的是message, 并没有使用counter, 却也会重新渲染; 同样的在Home子组件中修改了message, 根组件App也会重新渲染; 这是因为在默认情况下useSelector是监听的整个state, 当state发生改变就会导致组件重新渲染
要解决这个问题就需要使用useSelector的第二个参数来控制是否需要重新渲染, 我们只需要在useSelector函数中传入react-redux库中的
shallowEqual
函数即可, 它内部会自动进行一个浅层比较, 当使用的state中的数据确实发生变化的时候才会重新渲染
import React,{ memo }from'react'import{ useDispatch, useSelector, shallowEqual }from'react-redux'import{ changeMessageAction, changeNumberAction }from'./store/modules/counter'// 子组件Homeconst Home =memo(()=>{
console.log("Home组件重新渲染")const{ message }=useSelector((state)=>({message: state.counter.message
}), shallowEqual)const dispatch =useDispatch()functionchangeMessage(){dispatch(changeMessageAction("Hello ChenYq"))}return(<div><h2>{message}</h2><button onClick={changeMessage}>修改message</button></div>)})// 根组件Appconst App =memo(()=>{
console.log("App组件重新渲染")// 通过useSelector获取到store中的数据const{ counter }=useSelector((state)=>({counter: state.counter.counter
}), shallowEqual)// useDispatch获取到dispatch函数const dispatch =useDispatch()functionchangeNumber(num){dispatch(changeNumberAction(num))}return(<div><h2>当前计数:{counter}</h2><button onClick={()=>changeNumber(1)}>+1</button><button onClick={()=>changeNumber(-1)}>-1</button><Home/></div>)})exportdefault App
版权归原作者 学全栈的灌汤包 所有, 如有侵权,请联系我们删除。