会简单介绍Redux,然后使用纯Redux (不是使用react-redux) 写一个例子
目前使用版本
"redux": "^4.0.5"
Redux
- 1.单一数据源
Redux思想里面,一个应用永远只有一个数据源。
- 2.状态只读
数据源里面的state,只能读。
- 3.状态修改均由纯函数完成
在 Redux 里,我们通过定义 reducer 来确定状态的修改,而每一个 reducer 都是纯函数,这意味着它没有副作用,即接受一定的输入,必定会得到一定的输出。
流程图
由图中可以看出,Store 是一个中心(数据源),通过Reducer创建Store的state,在由Store把state传递给Components(组件),Components(组件)在通过事件触发Action来进行变更 Store 里面state。
Redux中存在几个概念:state, action, dispatch
- state: 组件的内部的状态
- action: 组件动作,相应的改变组件内部的状态值
- dispatch: 发出相应的动作
核心API
- getState() : 获取State
获取 store 中当前的状态。
- dispatch(action) : 执行Action,并返回这个Action
分发一个 action,并返回这个 action,这是唯一能改变 store 中数据的方式。
- subscribe(listener):监听Store变化
注册一个监听者,它在 store 发生变化时被调用。
- replaceReducer(nextReducer)
更新当前 store 里的 reducer,一般只会在开发模式中调用该方法。
- createStore(reducers[, initialState]) : 创建Store
要想生成 store,必须要传入 reducers,同时也可以传入第二个可选参数初始化状态(initialState)
- combineReducers(...)
是用来生成调用你的一系列 reducer , 每个 reducer 根据它们的 key 来筛选出 state 中的一部分数据并处理
reducers :是一个函数,用来变更Store的值。
函数样板:
reducer(previousState, action) => newState
可以看出,reducer 在处理 action 的同时,还需要接受一个 previousState 参数。所以,reducer 的职责就是根据 previousState 和 action 计算出新的 newState。
function(state,action) {
switch(action.type) {
case: ADD_TYPE:
// 处理具体的逻辑...
return { ...state }
}
}
完整Redux例子(TodoList):
ps: 这个例子不使用 react-redux, 而是直接使用 redux
创建一个react项目
npx create-react-app redux-dome
引入如下依赖
// 只用antd组件库
npm install antd --save
// 配置自定义
yarn add react-app-rewired customize-cra
// 组件按需加载
yarn add babel-plugin-import
// less 样式
yarn add less less-loader
项目根目录创建config-overrides.js
用于修改默认配置, 并加入如下代码
const { override, fixBabelImports } = require('customize-cra');
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: 'css',
}),
);
修改package.json 里面的启动配置,替换成我们的 react-app-rewired 的方式
// 将原来的 scripts 节点下启动方式替换如下
...
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test"
},
...
编写出一个Todo页面,TodoList.js
// list/TodoList.js
import React, { Component } from 'react'
import { Input, Button, List } from 'antd';
/**
* TodoList 一个列表
*/
class TodoList extends Component {
constructor(props) {
super(props)
this.inpVal = React.createRef();
this.state = {
data: [
"这是第一行",
"这是第二行",
"这是第三行"
],
}
}
addData() {
const inputValue = this.inpVal.current.state.value
console.log('当前值:', inputValue)
if (inputValue === undefined) {
return
}
this.setState({ data: this.state.data.concat(inputValue) })
this.inpVal.current.state.value = undefined
}
render() {
return (
<>
(
{item}
)}
/>
>
)
}
}
export default TodoList
App.js文件修改
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './list/TodoList';
ReactDOM.render( , document.getElementById('root'));
运行起来
npm start
输入内容,然后点击确认,可以添加到 list 上
引入 Redux
npm install --save redux
创建 action.js 组件的动作
// store/action/action.js
// action.js 组件的动作
// 定义动作类型(action_type)
export const ADD_TYPE = "ADD_TYPE"
/**
* 组件内的动作
*
* 也就是我这个组件要发起一个什么样的动作
* 比如我这里是发起的一个添加内容的操作
* @param text 内容
* @return 返回我这个动作的类型以及需要传递的内容
*/
function addText(text) {
return {
type: ADD_TYPE,
text
}
}
export { addText }
创建 reducer.js文件 发出相应的动作
// store/dispatch/reducer.js
// reducer.js 发出相应的动作
import { ADD_TYPE } from '../action/action'
import { combineReducers } from 'redux'
// 数据仓库
const defalutState = {
data: [
"这是第一行",
"这是第二行",
"这是第三行"
]
}
/**
* 发出相应的动作
* 这里的动作是直接针对 store 的操作
* @param {*} state
* @param {*} action
*/
function add(state = defalutState, action) {
// 判断一下 action 是什么操作类型
switch (action.type) {
case ADD_TYPE:
/**
* 处理完一些逻辑后,需要把这个state返回出去,最好返回一个新的 state
*/
console.log('action的add函数--text:', action.text)
return {
...state,
data: state.data.concat(action.text)
}
default:
}
// 不管何时,这个 state 都是返回的出去的
return state
}
/**
* combineReducers(...) 函数,是用来生成调用你的一系列 reducer
* 每个 reducer 根据它们的 key 来筛选出 state 中的一部分数据并处理
*/
const apps = combineReducers({
/**
* 把你的定义的一些列相应动作都写入
*/
add
})
export default apps
创建 store, 数据源
// store/index.js
import { createStore } from 'redux'
import reducers from './dispatch/reducer'
// 创建 store
const store = createStore(reducers)
export default store
修改 TotoList
import React, { Component } from 'react'
import { Input, Button, List } from 'antd';
// store 相关 start
import store from '../store/index'
import { addText } from '../store/action/action'
// store 相关 end
/**
* TodoList 一个列表
*/
class TodoList extends Component {
constructor(props) {
super(props)
// 打印所有的 store
// console.log(store.getState())
this.inpVal = React.createRef();
this.state = {
// data: [
// "这是第一行",
// "这是第二行",
// "这是第三行"
// ],
data: store.getState().add.data || []
}
}
componentDidMount() {
// 每次 state 更新时,打印日志
// 注意 subscribe() 返回一个函数用来注销监听器
this.unsubscribe = store.subscribe(() =>
this.setState({ data: store.getState().add.data }),
console.log('监听:', store.getState())
)
}
componentWillUnmount(){
// 停止监听
this.unsubscribe()
}
addData() {
const inputValue = this.inpVal.current.state.value
console.log('当前值:', inputValue)
if (inputValue === undefined) {
return
}
// this.setState({ data: this.state.data.concat(inputValue) })
store.dispatch(addText(inputValue))
this.inpVal.current.state.value = undefined
}
render() {
return (
<>
(
{item}
)}
/>
>
)
}
}
export default TodoList
然后在运行
关键点在于componentDidMount(){...}
生命周期里面的监听
// 每次 state 更新时,打印日志
// 注意 subscribe() 返回一个函数用来注销监听器
this.unsubscribe = store.subscribe(() =>
this.setState({ data: store.getState().add.data }),
console.log('监听:', store.getState())
)
在componentWillUnmount(){...}
生命周期函数需要释放redux的state监听
// 停止监听
this.unsubscribe()
总结
通过上面的看出,使用纯 Redux 难度曲线比较高,而且繁琐!
通过store.subscribe()
进行监听,会带来性能消耗,而且会造成额外的UI界面的渲染!
所以不建议直接使用 Redux 库,而是使用 React-Redux 库来搭配 react 使用!
demo: https://github.com/weiximei/redux-demo