redux
React只是DOM的一个抽象层
Web应用是一个状态机,视图与状态是一一对应的
所有的状态,保存在一个对象里面(唯一数据源)
需要使用redux的项目
- 用户的使用方式复杂
- 不同身份的用户有不同的使用方式(比如普通用户和管理员)
- 多个用户之间可以协作
- 与服务器大量交互,或者使用了WebSocket
- View要从多个来源获取数据
从组件层面考虑,什么样子的需要redux:
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
redux有四个组成部分
store:用来存储数据
reducer:真正的来管理数据
actionCreators:创建action,交由reducer处理
view:用来使用数据
redux的流程
1)store通过reducer创建了初始状态
2)view通过store.getState()获取到了store中保存的state挂载在了自己的状态上
3)用户产生了操作,调用了actions 的方法
4)actions的方法被调用,创建了带有标示性信息的action
5)actions内部通过调用store.dispatch方法将标志性的action发送到了reducer中
6)reducer接收到action并根据标识信息判断之后返回了新的state
7)store的state被reducer更改为新state的时候,store.subscribe方法里的回调函数会执行,此时就可以通知view去重新获取state
例如创建如下案例
在文本框输入文字后,按下回车键,添加到列表中
store通过reducer创建了初始状态,在reducer中定义一个数组
store/index.js
import {createStore} from 'redux';
import reducer from './reducer';
const store = createStore(reducer)
export default store
store/reducer.js
let state = {
list: [
{ id: 1, title: "星期一", isFinished: true },
{ id: 2, title: "星期二", isFinished: false }
]
}
const reducer = (prevState = state, action) => {
//让上一条数据默认为state
let newState = { ...prevState }
return newState
}
view通过store.getState()获取到了store中保存的state挂载在了自己的状态上
在TodoContent组件中展示数据
import React, { Component } from 'react'
import store from '../store'
import TodoInput from './TodoInput'
export default class Redux extends Component {
constructor() {
super()
this.state = {
list: []
}
}
setList = () => {
//从store的getState()方法中获取到list并更改state中的list数据
this.setState({
// Store对象包含所有数据。如果想得到某个时点的数据,就要对 Store 生成快照。当前时刻的 State,可以通过store.getState()拿到。
list: store.getState().list
})
}
componentDidMount() {
this.setList()
//Store 允许使用store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数。当state的数据改变时,就去执行setList,由此state的数据也会改变,render也会重新渲染
store.subscribe(() => {
this.setList()
})
}
handleCheck = (id) => {
actions.changeList(id)
}
remove = (id) => {
actions.removeList(id)
}
render() {
let { list } = this.state
return (
<div>
<TodoInput />
{
list.map(item => {
return <li key={item.id} style={{textDecoration:item.isFinished ? 'line-through':'none'}}><input type="checkbox" checked={item.isFinished} onChange={this.handleCheck.bind(this, item.id)} /> {item.title}
<button onClick={this.remove.bind(this, item.id)}>删除</button>
</li>
})
}
</div>
)
}
}
当input框输入文本时,按下键盘下的Enter,列表会增加一条
也就是当用户产生了操作,调用了actions 的方法
组件TodoInput
import actionCreators from "../../store/actionCreators"
export default class TodoInput extends Component {
handleKey=e=>{
if(e.keyCode==13){
// 当按下Enter键时,触发actionCreators下的addNewTode
actionCreators.addList(e.target.value)
e.target.value=""
}
}
render() {
return (
<input placeholder="请输入内容" onKeyUp={this.handleKey}/>
)
}
}
actions的方法被调用,创建了带有标示性信息的action
actions内部通过调用store.dispatch方法将标志性的action发送到了reducer中
actionCreators.js
const actionCreators = {
addList(title){
//需要定义一个具有特殊标识的action对象
let action = {
type:"addList",
title
}
//需要将action对象派发给reducer
store.dispatch(action)
},
changeList(id) {
let action = {
type: "changeList",
id
}
store.dispatch(action)
},
removeList(id) {
let action = {
type: "removeList",
id
}
store.dispatch(action)
},
}
export default actions
reducer接收到action并根据标识信息判断之后返回了新的state,store.subscribe方法里的回调函数会执行,此时就可以通知view去重新获取state
reducer.js
let state = {
list: [
{ id: 1, title: "星期一", isFinished: true },
{ id: 2, title: "星期二", isFinished: false }
]
}
const reducer = (prevState = state, action) => {
let newState = { ...prevState }
switch (action.type) {
case "addList":
newState.list.push({
id: handleList.getId(newState.list),
title: action.title
})
break;
case "changeList":
newState.list = handleList.changeList(newState.list, action.id)
break;
case "removeList":
newState.list = handleList.removeList(newState.list, action.id)
break;
default:
break;
}
return newState
}
const handleList = {
getId(list) {
list = list.slice()
if (list.length === 0) return 1
return list.sort((a, b) => {
return b.id - a.id
})[0].id + 1
},
changeList(list, id) {
list = list.slice()
for (let i = 0; i < list.length; i++) {
if (list[i].id === id) {
list[i].isFinished = !list[i].isFinished
}
}
return list
},
removeList(list, id) {
list = list.slice()
return list.filter((item) => {
console.log(item)
if (item.id === id) {
return false
}
return true
})
}
}
export default reducer