这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战
redux
之前在学习react的时候,兄弟组件之间传状态都是要通过父组件来进行传递。redux可以解决这个问题,它可以让某个组件的状态让其他组件可以随时拿到(共享),在一个组件需要改变另一个组件的状态的时候就可以用redux。但是由于redux的实现操作比较复杂,能不用就不用,但还是要学。
redux是一个专门用于做状态管理的JS库(不是react插件库)- 作用:集中式管理react引用中多个组件共享的状态
一、redux工作流程
组件可以分发任务和传递之前的状态值给Reducers来进行处理,Reducers刚开始会初始化状态,然后再对状态进行加工,加工时根据旧的state和action产生新的纯函数,处理之后把新的状态值返回给Store,进而传递给组件。在这里,store是把state、action、reducer联系起来的一个很重要的对象。
action(动作的对象)
type:标识属性,值为字符串,唯一且是必填data:数据属性,值类型任意,可选
二、redux的使用
-
创建一个store
在
src创建一个redux的文件夹,再在里面创建一个store.js;这个文件专门用于暴露一个store对象,整个应用只有一个store对象- 引入
createStore,专门用于创建redux中最为核心的store对象 - 引入为Count组件服务的
reducer - 暴露
store对象
import { createStore } from 'redux' import countReducer from './count_reducer' export default store = createStore(countReducer); - 引入
-
有了store的同时也要有reducer
在
redux中创建一个reducer.js文件,该文件是用于创建一个为某个组件服务的reducer,reducer的本质就是一个函数;reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action);reducer有两个作用:初始化状态、加工状态最开始的状态值为
undefine或为空时就要初始化状态;之后就从action对象这种获取type和data,这里用switch来对不同的type进行处理;const initState = 0; export default function countReducer(preState = initState, action) { const { type, data } = action; switch (type) { case 'increment': return preState + data default: return preState } }
-
创建
actionCreator,在redux中创建一个actionCreator.js文件,用于创建一些要进行的动作,专门为组件生成action对象,记得要暴露;例如:export const createIncrementAction = data => ({ type: 'increment', data })
如果方法类型比较多,为了防止写错,可以创建一个文件专门来定义这些方法的变量名,同时也便于管理;例:export const INCREMENT = 'increment';
在组件中引入刚刚创建的store.js文件,通过store.getState就可以获取redux中所保存的状态;
// 纯react写的加法
increment = () => {
const { value } = this.selectNumber;
const { count } = this.state;
this.setState({ count: count + parseInt(value) })
}
// 用了redux之后写的加法
increment = () => {
const { value } = this.selectNumber;
store.dispatch(createIncrementAction(value))
}
此时如果调用了这个函数,状态就会发生改变,但是在redux中,它只是帮我们管理状态,并不会更新页面;因此在这里需要手动去调用render(),于是我们就需要用到一个生命周期钩子——componentDidMount,它可以检测redux中状态的变化,只要变化就会调用render
componentDidMount() {
store.subscribe(() => {
this.setState({})
})
}
如果在用生命周期钩子实现的话,在每个组件中都要写上面这段代码,为了避免这个麻烦,也可以在渲染App组件的index.js中包一个订阅的函数;
ReactDOM.render(<App />, document.getElementById('root'))
store.subscribe(() => {
ReactDOM.render(<App />, document.getElementById('root'))
})
异步action
同步action在返回的时候是一个对象,而异步的action返回的时候是一个函数;原来同步的时候定时器写在组件里面的方法中,而异步的话
-
在组件中调用
store.dispatch的时候传值和时间给action,store.dispatch(createIncrementAsyncAction(value, 500)) -
在action中调用定时器;
export const createIncrementAsyncAction = (data, time) => { return () => { setTimeout(() => { dispatch({ type: INCREMENT, data }) }, time); } }
- 因为store本来只能接收的是一个对象而不是函数
- 下载一个
npm add redux-thunk库,在store中引入thunk, - 同时要在
redux中多引入一个applyMiddleware - 暴露store:
export default createStore(countReducer, applyMiddleware(thunk));