Redux
- Redux是将整个应用状态存储到store,store里存在一个状态树state tree
- 组件可通过store.dispatch派发行为action给store,store不会直接修改state,而是通过用户编写的reducer来生成新的state,并返回给store
- 其他组件通过订阅store中的state状态来刷新试图
注意点:
- 整个应用有且只有一个store,其内部的state tree存储整个应用的state
- state是只读的,修改state只能通过派发action,需要通过reducer纯函数描述action如何修改state的
- 单一数据源的设计让React组件之间通信更加方便,也有利于状态的统一管理
先看一个计数器的例子:
store.js
import {
createStore} from 'redux'
export const ADD = 'ADD'
export const MINUS = 'MINUS'
const reducer = (state = {
count:0}, action) => {
console.log('action', action)
switch(action.type){
case ADD:
return {
count: state.count + 1}
case MINUS:
return {
count: state.count - 1}
default:
return state
}
}
let store = createStore(reducer)
export default store
1.关于store
通过createStore可以创建一个store,方法里需要传入参数reducer。创建的store是一个对象,有以下方法可以调用:
- store.getState(): 获取最新的state tree
- store.dispatch(): 派发行为action
- store.subscribe(): 订阅store中state的变化
2.关于reducer
reducer必须是个纯函数,接收state和action。state是旧状态,根据action.type的不同,生成新的state并返回
Counter.jsx
import {
useEffect, useState } from 'react'
import store from './store'
const Counter = () => {
//通过getState方法获取count值
const [num, setNum] = useState(store.getState().count)
useEffect(()=>{
//通过subscribe实现订阅,当store中state状态发生变化时,就将新值传入
const unSubscribe = store.subscribe(()=>{
setNum(store.getState().count)
})
return () => {
//取消订阅
unSubscribe && unSubscribe()
}
},[])
return (
<div>
<p>{
num}</p>
//通过dispatch派发action给store
<button onClick={
()=>{
store.dispatch({
type: 'ADD'})}}>+</button>
<button onClick={
()=>{
store.dispatch({
type: 'MINUS'})}}>-</button>
</div>
)
}
export default Counter
这样一个简单的Counter组件就实现了。
Redux内部就是使用了’发布-订阅’模式。
3.combineReducers
当一个应用中包含多个模块,将所有模块的state放在一起并不合理,更好的做法是按照模块进行划分,每个模块都有自己的reducer和action,最终通过Redux的combineReducers合并成一个大的reducer。
combineReducers方法接收一个对象,属性key可任意设置,value对应每个模块的reducer函数,最终返回一个合并之后的reducer方法。
import {
createStore, combineReducers} from 'redux'
export const ADD = 'ADD'
export const MINUS = 'MINUS'
export const SUM_ADD = 'SUM_ADD'
export const SUM_MINUS = 'SUM_MINUS'
const countReducer = (state = {
count:0}, action) => {
console.log('action', action)
switch(action.type){
case ADD:
return {
count: state.count + 1}
case MINUS:
return {
count: state.count - 1}
default:
return state
}
}
const sumReducer = (state = {
sum:0}, action) => {
console.log('action', action)
switch(action.type){
case SUM_ADD:
return {
sum: state.sum + 1}
case SUM_MINUS:
return {
sum: state.sum - 1}
default:
return state
}
}
const combineRe = combineReducers({
countReducer, sumReducer})
let store = createStore(combineRe)
export default store
import {
useEffect, useState } from 'react'
import store from './store'
const Counter = () => {
//注意这里的取值
const [num, setNum] = useState(store.getState().countReducer.count)
const [sum, setSum] = useState(store.getState().sumReducer.sum)
useEffect(()=>{
const unSubscribe = store.subscribe(()=>{
setNum(store.getState().countReducer.count)
setSum(store.getState().sumReducer.sum)
})
return () => {
unSubscribe && unSubscribe()
}
},[])
return (
<div>
<p>{
num}</p>
<button onClick={
()=>{
store.dispatch({
type: 'ADD'})}}>+</button>
<button onClick={
()=>{
store.dispatch({
type: 'MINUS'})}}>-</button>
<p>{
sum}</p>
<button onClick={
()=>{
store.dispatch({
type: 'SUM_ADD'})}}>+</button>
<button onClick={
()=>{
store.dispatch({
type: 'SUM_MINUS'})}}>-</button>
</div>
)
}
export default Counter
如果在store.js中我们这样设置combineReducers:
const combineRe = combineReducers({
a:countReducer,
b:sumReducer
})
那么在接下来的组件获取值时就应该这么用:
store.getState().a.count
store.getState().b.sum
reducer合并之后,store中的state tree也会按照模块进行划分。
当组件中派发action时,action会传递到combineReducers返回的函数中,在该函数中,会调用每个模块各自的reducer生成各自新的state, 最终将所有state合并之后,去更新store中的state。
总结
在React组件中使用store,需要手动引入store文件,手动订阅store中状态的变化,这样并不合理,所以我们需要引入react-redux来解决问题。