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
```javascript
const 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)
//获取state
this.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,不能改变state
if(action.type==='change_input_value'){
let newState=JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
// reducer处理完业务逻辑返回给store
return 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()方法传递给store
store.dispatch(action)
}
配置redux devTools
// 创建数据存储仓库,store/index.js
const 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 })
- 导入并调用
```javascript
import {changeCreateAction,addCreateAction,delCreateAction} from '../store/actionCreators'
changeInput(e){
const action=changeCreateAction(e.target.value)
// 将action传递给store
store.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.js
import { 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.value
return 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.value
return newState
}
// reducer把数据返回给store
return 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))
//创建store
const 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) }