react是视图层框架,redux是数据层框架,管理数据状态和UI状态的JavaScript应用工具
通俗理解:
创建store
- 在src目录下创建/store/index.js ```javascript import {createStore} from ‘redux’ import reducer from ‘./reducer’
// 创建数据存储仓库 const store=createStore(reducer)
export default store
- 在store目录下创建reducer```javascriptconst defaultState={inputValue:'请输入',listData:['早8点开晨会,分配今天的开发工作','早9点和项目经理作开发需求讨论会','晚5:30对今日代码进行review']}export default ((state=defaultState,action)=>{return state})
使用store
import React, { Component } from 'react';import store from '../store/index'class TodoList extends Component{constructor(props){super(props)//获取statethis.state=store.getState()}render(){return(<div><div><Input value={this.state.inputValue} style={{width:250,marginRight:10}}/><Button type="primary">增加</Button></div><List bordered dataSource={this.state.listData}renderItem={(dataItem)=>(<List.Item>{dataItem}</List.Item>)}/></div>)}}
修改store
reducer.js
export default ((state=defaultState,action)=>{// reducer处理,只能接收state,不能改变stateif(action.type==='change_input_value'){let newState=JSON.parse(JSON.stringify(state))newState.inputValue = action.value// reducer处理完业务逻辑返回给storereturn newState}return state})
组件.js
constructor(props){super(props)this.state=store.getState()//订阅Redux的状态,括号内为订阅之后执行的// 只有执行了该函数,state的值在组件中才会改变store.subscribe(()=>{this.setState(store.getState())})}changeInput(e){const action={type:'change_input_value',value:e.target.value}//store只是一个仓库,它并没有管理能力,它会把接收到的action自动转发给Reducer//action通过dispatch()方法传递给storestore.dispatch(action)}
配置redux devTools
// 创建数据存储仓库,store/index.jsconst store=createStore(reducer,window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
技巧:
actionType独立出来
actionType单独建立一个文件:
export const CHANGE_INPUT='change_input_value'export const ADD_ITEM='add_value'export const DEL_ITEM='del'
导入并使用
import {CHANGE_INPUT,ADD_ITEM,DEL_ITEM} from '../store/actionTypes'add(){const action={type:ADD_ITEM}store.dispatch(action)}
Redux Action和业务逻辑分离
新建store目录下的actionCreator.js ```javascript import {CHANGE_INPUT,ADD_ITEM,DEL_ITEM} from ‘../store/actionTypes’
export const changeCreateAction=(value)=>({ type:CHANGE_INPUT, value }) export const addCreateAction=()=>({ type:ADD_ITEM }) export const delCreateAction=(value)=>({ type:DEL_ITEM, value })
- 导入并调用```javascriptimport {changeCreateAction,addCreateAction,delCreateAction} from '../store/actionCreators'changeInput(e){const action=changeCreateAction(e.target.value)// 将action传递给storestore.dispatch(action)}
原则:
- store是唯一的,不能同时存在多个store
- 只有store能改变自己的内容,reducer不能改变:store拿到了Reducer的数据,自己对自己进行了更新。
- Reducer必须是纯函数:调用参数相同,返回相同
组件UI和业务逻辑拆分:
- ui和逻辑分离
- 逻辑通过props属性传递到ui组件里
无状态组件:
有render方法并且没有state和业务逻辑的组件,可以提升性能
import React from 'react';import 'antd/dist/antd.css'import { Input , Button , List } from 'antd'const TodoListUI=(props)=>{return(<div><div><Input value={props.inputValue} onChange={props.changeInput} placeholder={props.inputValue} style={{width:250,marginRight:10}}/><Button type="primary" onClick={props.add}>增加</Button></div><List bordered dataSource={props.listData}renderItem={(dataItem,index)=>(<List.Item onClick={()=>{props.delItem(index)}}>{dataItem}</List.Item>)}/></div>)}export default TodoListUI
从后端接口调取数据和redux结合
- 配置后端(egg.js)接口 ```javascript /* @type Egg.EggPlugin / module.exports = { cors:{ enable:true, package:’egg-cors’ } }; //config.default.js config.security={ domainWhiteList: [ ‘http://localhost:3000‘ ] } //controller const Controller = require(‘egg’).Controller;
class HomeController extends Controller { async index() { const { ctx } = this; ctx.body = ‘hi, egg’; } async getListData(){ const {ctx}=this ctx.body=[ “事项1”, “事项2”, “事项3”, “事项4”, ] } async addListData(){
} async delListData(){
} }
module.exports = HomeController; //路由 module.exports = app => { const { router, controller } = app; router.get(‘/‘, controller.home.index); router.get(‘/getListData’, controller.home.getListData); };
- 前端```javascript//reducer.jsimport { CHANGE_INPUT,ADD_ITEM,DEL_ITEM,GET_LIST} from "./actionType";const defaultState={inputValue:''}export default ((state=defaultState,action)=>{let newState=JSON.parse(JSON.stringify(state))// reducer接收到action后进行业务逻辑处理if(action.type===CHANGE_INPUT){newState.inputValue=action.valuereturn newState}else if(action.type===ADD_ITEM){newState.listData.push(state.inputValue)return newState}else if(action.type===DEL_ITEM){newState.listData.splice(action.value,1)return newState}else if(action.type===GET_LIST){newState.listData=action.valuereturn newState}// reducer把数据返回给storereturn state})//==============todoList.jsx====================================componentDidMount(){axios.get('http://127.0.0.1:7001/getListData').then(res=>{const action=getListCreate(res.data)store.dispatch(action)})}
redux中间件:
redux-thunk

- 在Dispatch一个Action之后,到达reducer之前,进行一些额外的操作
- 在实际工作中你可以使用中间件来进行日志记录、创建崩溃报告,调用异步接口或者路由
步骤:
- 安装:npm install —save redux-thunk
- 在store/index中导入
import thunk from 'redux-thunk' 配置:
- 引入applyMiddleware使用中间件
import { createStore,applyMiddleware,compose} from "redux"; - 添加增强函数(相当于链式调用)
const composeEnhancer=window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose// 增强函数==链式函数【可以同时使用redux dev tools和中间件】const enhancer=composeEnhancer(applyMiddleware(thunk))//创建storeconst store=createStore(reducer,enhancer)
- 引入applyMiddleware使用中间件
使用:
- 在actionCreators.js中编写业务逻辑,此时action可以是一个函数 ```javascript import axios from ‘axios’
export const getTodoList = () =>{ return (dispatch)=>{ axios.get(‘https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList').then((res)=>{ const data = res.data // 返回一个对象类型的action const action = getListCreate(listData) // 自动传到store中 dispatch(action) }) } } }) } } //=======
//组件.js先引入getTodoList import {getTodoList , changeInputAction , addItemAction ,deleteItemAction,getListAction} from ‘./store/actionCreatores’ componentDidMount(){ // 返回函数的action const action = getTodoList() store.dispatch(action) }
