1、流程
先看张redux工作流程图
刚开始看我也有点懵,把他比喻成生活中的一个小例子就好理解了,比如图书馆借书的例子:
首先简单的把components比做成借书的人
当借书人(components)说:我要借书,那么说话的这个行为就是actions
这时候图书管理员(Store)听见了,他来集中管理书籍的信息、数据
图书管理员(Store)也不知道你要借哪一本书、有没有这本书,他要求查一下,看下手册(reducers),
最后告诉管理员(Store)关于这本书的数据信息
1、首先我们的组件要访问Store里面的数据,这时候要获取数据的这个行为就会触发Action Creators
2、随后Action Creators通过一些其他的function告诉Store要获取某些数据,这时候Store也不知道要获取哪些数据
3、这时候Reducers查一下并告诉Store要返回哪些数据给组件,Store知道后把数据返回给Components
2、入门使用
创建store.js和reducer.js
store.js代码
import { createStore } from 'redux'
import Reducer from './reducer'
// window下有这个变量则执行调试工具
const Store = createStore(
Reducer, /* preloadedState, */
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
export default Store
reducer.js代码
const defaultState = {
inputVal: '666',
list: [1,2,3]
}
// reducer中需要返回一个函数,接收旧的 state 和 action,返回新的 state
//reducer可以接收state,但是不能修改state
export default (state = defaultState, action) => {
return state
}
然后在页面中引入store
import Store from '../store'
通过getState()
来获取Store中的数据
通过subscribe()
在组件内更新Store
通过dispatch()将修改后的数据传给Store
import React, { Component } from 'react'
import Store from '../store'
import { Button, Input, List } from 'antd'
export default class TodoList extends Component {
constructor(props){
super(props)
let { list, inputVal } = Store.getState()
this.state = {
list, inputVal
}
}
render() {
const { list, inputVal } = this.state
return (
<div>
<h1>rodolist</h1>
<Input value={inputVal} placeholder="Basic usage" style={{width:'300px'}} />
<Button type="primary">Primary Button</Button>
<List
size="default"
bordered
dataSource={list}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
)
}
}
现在我们想在输入框中输入一条数据点击按钮添加到列表中,上代码
todoList.js
import React, { Component } from 'react'
import Store from '../store'
import { Button, Input, List } from 'antd'
export default class TodoList extends Component {
constructor(props){
super(props)
//利用getState()获取store中的数据
// console.log(Store.getState())
let { list, inputVal } = Store.getState()
this.state = {
list, inputVal
}
}
componentDidMount(){
//store发生变化就更新
Store.subscribe(this.handleStoreChange)
}
componentDidUpdate(){
Store.subscribe(this.handleStoreChange)
}
handleInputChange(e){
const action = {
type: 'changeInputVal',
value: e.target.value
}
//将修改后的数据传给store
Store.dispatch(action)
}
handleStoreChange = () => {
let tVal = Store.getState()
console.log('tVal',tVal)
this.setState({
inputVal: tVal.value,
list: tVal.list
})
}
onClickSubmit = () => {
const { inputVal } = Store.getState()
console.log(inputVal)
//为空不添加
if (!inputVal) return
const action = {
type: 'addItem'
}
Store.dispatch(action)
this.setState({
inputVal: ''
})
}
render() {
const { list, inputVal } = this.state
return (
<div>
<h1>rodolist</h1>
<Input value={inputVal} onChange={(e) => this.handleInputChange(e)} placeholder="Basic usage" style={{width:'300px'}} />
<Button type="primary" onClick={() => this.onClickSubmit()}>Primary Button</Button>
<List
size="default"
bordered
dataSource={list}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
)
}
}
完整reducer.js代码
const defaultState = {
inputVal: '666',
list: [1,2,3]
}
// reducer中需要返回一个函数,接收旧的 state 和 action,返回新的 state
export default (state = defaultState, action) => {
console.log('action',action)
if (action.type === 'changeInputVal') {
//reducer可以接收state,但是不能修改state;所以要进行一次深拷贝
let newState = JSON.parse(JSON.stringify(state))
newState.inputVal = action.value
return newState
}
if (action.type === 'addItem') {
let newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputVal)
newState.inputVal = ''
console.log('newState',newState)
return newState
}
return state
}
在调试工具中看到数据也是正确的
3、Action代码拆分、优化
回头看一下写的代码,我自己看着都难受;考虑到以后万一action很多呢,那不是要累死;还有另一种情况:万一type名称拼写错了,也要找半天,所以这样是不可取的应该拆分出来
拆分之前先来分析应该要怎么拆分?
我们可以看到每一个action都有一个type、value;然后reducer里面都会有对应的逻辑处理,根据这两点就容易多了
继续在store目录下新建两个文件
actionTypes.js
export const changeInputVal = 'changeInputVal'
export const addItem = 'addItem'
export const deleteItem = 'deleteItem'
actionCreators.js
import { changeInputVal, addItem, deleteItem } from './actionTypes'
export const getInputChangeAction = (value) => ({
type: changeInputVal,
value
})
export const getOnSubmitAction = () => ({
type: addItem
})
export const getDeleteItemAction = (index) => ({
type: deleteItem,
index
})
reducer.js代码
import { changeInputVal, addItem, deleteItem } from './actionTypes'
const defaultState = {
inputVal: '',
list: []
}
export default (state = defaultState, action) => {
if (action.type === changeInputVal) {
let newState = JSON.parse(JSON.stringify(state))
newState.inputVal = action.value
return newState
}
if (action.type === addItem) {
let newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputVal)
newState.inputVal = ''
return newState
}
if (action.type === deleteItem) {
let newState = JSON.parse(JSON.stringify(state))
newState.list.splice(action.index,1)
return newState
}
return state
}
在todolist引入
如果方法少可以这样,多的话直接import * as xxxx from xxxxxx
import { getInputChangeAction, getOnSubmitAction, getDeleteItemAction } from '../store/actionCreators'
直接在对应的方法下调用并传参即可
onClickSubmit = () => {
const action = getOnSubmitAction()
Store.dispatch(action)
}
handleInputChange(e){
const action = getInputChangeAction(e.target.value)
Store.dispatch(action)
}
deleteItem = (index) => {
Store.dispatch(getDeleteItemAction(index))
}