react-redux
redux
redux也是一个架构思维, 在这个架构思维中 React 充当是 视图 V
先来看图:
-
Store 数据的管理者和数据的存储者
-
actionCreators 动作的创建者和发送者,发送动作给 reducers
-
react Components 组件( 用来充当视图层 ),进行用户交互
-
reducers 数据的修改者,返回一个新的 newstate 给store
React 只是 DOM 的一个抽象层,并不是 Web 应用的完整解决方案。有两个方面,它没涉及。
- 代码结构
- 组件之间的通信
2013年 Facebook 提出了 Flux 架构的思想,引发了很多的实现。2015年,Redux 出现,将 Flux 与函数式编程结合一起,很短时间内就成为了最热门的前端架构。
如果你不知道是否需要 Redux,那就是不需要它
只有遇到 React 实在解决不了的问题,你才需要 Redux
简单说,如果你的UI层非常简单,没有很多互动,Redux 就是不必要的,用了反而增加复杂性。
Redux的设计思想:
- Web 应用是一个状态机,视图与状态是一一对应的。
- 所有的状态,保存在一个对象里面(唯一数据源)。
注意:flux、redux都不是必须和react搭配使用的,因为flux和redux是完整的架构,在学习react的时候,只是将react的组件作为redux中的视图层去使用了。
Redux的使用的三大原则:
- Single Source of Truth(唯一的数据源)
- State is read-only(状态是只读的)
- Changes are made with pure function(数据的改变必须通过纯函数完成)
Redux的流程:
- store通过reducer创建了初始状态
import { createStore } from 'redux';
import reducer from "./reducer"; //引入reducer
const stort = createStore ( reducer ) //通过reducer创建了初始状态
export default stort;
- view通过store.getState()获取到了store中保存的state挂载在了自己的状态上
constructor () {
super()
this.state = {
todos : store.getState().todos //获取到了store中保存的state挂载在了自己的状态
}
}
- 用户产生了操作,调用了actions 的方法
import * as type from './type' //事件类型 ,单独拆了出来
import store from './index'
const actionCreators = {
add ( val ) { //调用该方法
/* 可以前后端数据交互 */
const action = {
type: type.ADDTODO,
payload: val // 负载
}
store.dispatch( action )
}
}
export default actionCreators
- actions的方法被调用,创建了带有标示性信息的action
- 上面的代码:创建了type类型为type.ADDTODO的add方法
- actions将action通过调用store.dispatch方法发送到了reducer中
store.dispatch( action ) //第 3 步中的
- reducer接收到action并根据标识信息判断之后返回了新的state
import state from './state'
import * as type from './type'
const reducer = ( previousState = state , action ) => {
const newState = { //初始化的赋值,操作数据不改变原数据(解构)
...previousState
}
switch ( action.type ) { //通过第2个参数action接收到类型
case type.ADDTODO: //判断,做数据修改或数据请求
newState.todos.push( {
id: newState.todos.length + 1,
text: action.payload
})
break;
default:
break;
}
return newState
}
export default reducer
- store的state被reducer更改为新state的时候,store.subscribe方法里的回调函数会执行,此时就可以通知view去重新获取state
//在view的componentDidMount钩子中
componentDidMount () {
store.subscribe( () => {
this.setState({
todos: store.getState().todos
})
})
}
** Reducer必须是一个纯函数:**
Reducer 函数最重要的特征是,它是一个纯函数。也就是说,只要是同样的输入,必定得到同样的输出。Reducer不是只有Redux里才有,之前学的数组方法reduce
, 它的第一个参数就是一个reducer
纯函数是函数式编程的概念,必须遵守以下一些约束。
- 不得改写参数
- 不能调用系统 I/O 的API
- 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果
redux使用流程 ( 例子:todolist – 增加一条数据 ) redux基础
- redux是一个架构思维,我们实现需要一个工具,这个工具叫做redux
- 安装redux
$ yarn add redux
- 在src下新建一个store,store中新建index.js用来打造
store
//打造store.
import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore( reducer ) // 不加new createStore() 参数不是一个 Object 而是一个Function
export default store
- 在store下新建一个state (存储数据)
const state = {
todos: [
{
id: 1,
task: '任务一'
}
]
}
export default state
- 在 store下新建一个 reducer
import state from './state'
const reducer = ( previousState = state , action ) => {
const newState = {
...previousState // 解构的原因是为了做深拷贝,我们操作newState,不会影响state
}
return newState //返回新的数据
}
export default reducer
- 在你想要使用的组件中直接引用 store
import React, { Component,Fragment } from 'react'
import store from '../store'
class Content extends Component{
constructor () {
super()
this.state = {
todos: store.getState() //进行初始化绑定
}
}
render () {
return (
<Fragment>
<div>
<ul>
<li> 1 </li>
</ul>
</div>
</Fragment>
)
}
}
export default Content
-
进行用户交互 React component — > actionCreators
v-->actions
-
在store下新建 actionCreators.js
import * as type from './type'
import store from './index'
const actionCreators = {
add_todos_item ( val ) {
const action = { //动作的创建
type: type.ADD_TODOS_ITEM,
payload: val // 负载数据
}
// 动作的发送
store.dispatch( action ) //引入store 使用dispatch方法进行定做的发送
}
}
export default actionCreators
- 在button组件中触发 actionCreators中 的方法
import React, { Component,Fragment } from 'react'
import actionCreators from './../store/actionCreators'; //引入actionCreators
class Button extends Component{
add = () => {
let val = this.input.value
actionCreators.add_todos_item( val ) //使用actionCreators中的方法。执行创建和发送动作
this.input.value = ''
}
render () {
return (
<Fragment>
<div>
<input type = "text" ref = { el => this.input = el } />
<br/>
<button onClick = { this.add }> + </button>
</div>
</Fragment>
)
}
}
export default Button
- 在 reducer 中修改数据
import state from './state'
import * as type from './type'
const reducer = ( previousState = state,action) => {
let newState = {
...previousState
}
switch ( action.type ) { //判断用户进行了那个用户交互 ,操作新状态
case type.ADD_TODOS_ITEM:
newState.todos.push({ //修改新状态
id: newState.todos.length + 1,
task: action.payload
})
break;
default:
break;
}
return newState
}
export default reducer
- 进行数据更新,通过store的订阅功能进行更新,也就是组件需要重新赋值一次,数据在Content组件中componentDidMount钩子里进行订阅
componentDidMount () {
store.subscribe( () => { //通过store.subscribe()方法
this.setState({
todos: store.getState().todos //订阅 更新数据
})
})
}
redux 基础 到此结束。end
在这里留下3个疑问,留在redux进阶
- 问题: 我们在更新视图时,在组件中每次都要进行 store 的订阅,代码是冗余的
- 问题: 我们在触发actionCreators中的方法时,触发形式的书写也很相似
- 问题: 后端数据交互往哪里写? actionCreators中写,actionCreators不是用来创建动作的么?