文章目录
redux理解
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 (如果你需要一个 WordPress 框架,请查看 Redux Framework。)
可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。不仅于此,它还提供 超爽的开发体验,比如有一个时间旅行调试器可以编辑后实时预览。
Redux 除了和 React 一起用外,还支持其它界面库。 它体小精悍(只有2kB,包括依赖)。
- 中文文档:redux
redux是什么
- redux是一个专门用于状态管理的JS库(不是react插件库)。
- 它可以用在react,angular,vue等项目中,但基本与react配合使用。
- 作用:集中式状态管理react应用中多个组件共享的状态。
什么情况下需要使用redux
- 某个组件的状态,需要让其他组件可以随时拿到(共享)。
- 一个组件需要改变另一个组件的状态(通信)。
- 总体原则:能不用就不用,如果不用比较吃力才考虑使用。
redux原理图:
redux三个核心概念
action
- 动作的对象
- 包含两个属性
- type:标识属性,值类字符串,唯一,必要属性。
- data:数据属性,值类型任意,可选属性
- 例子:
{type: 'ADD_STUDENT', data:{name:'tom', age:18}}
reducer
- 用于初始化状态、加工状态
- 加工时,根据旧的state和action,产生新的state的纯函数
store
- 将state、action、reducer联系在一起的对象
- 如何得到此对象?
- import {createStore} from ‘redux’
- Import reducer from ‘./reducers’
- Const stroe = createStore(reducer)
- 此对象的功能?
- getState():得到state
- dispatch(action):分发action,触发reducer调用,产生新的state
- subscribe(listener):注册监听,当产生了新的state时,自动调用
官方文档解释:
Redux 本身很简单。
当使用普通对象来描述应用的 state 时。例如,todo 应用的 state 可能长这样:
{
todos: [{
text: 'Eat food',
completed: true
}, {
text: 'Exercise',
completed: false
}],
visibilityFilter: 'SHOW_COMPLETED'
}
这个对象就像 “Model”,区别是它并没有 setter(修改器方法)。因此其它的代码不能随意修改它,造成难以复现的 bug。
要想更新 state 中的数据,你需要发起一个 action。Action 就是一个普通 JavaScript 对象(注意到没,这儿没有任何魔法?)用来描述发生了什么。下面是一些 action 的示例:
{ type: 'ADD_TODO', text: 'Go to swimming pool' }
{ type: 'TOGGLE_TODO', index: 1 }
{ type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }
强制使用 action 来描述所有变化带来的好处是可以清晰地知道应用中到底发生了什么。如果一些东西改变了,就可以知道为什么变。action 就像是描述发生了什么的指示器。最终,为了把 action 和 state 串起来,开发一些函数,这就是 reducer。再次地,没有任何魔法,reducer 只是一个接收 state 和 action,并返回新的 state 的函数。 对于大的应用来说,不大可能仅仅只写一个这样的函数,所以我们编写很多小函数来分别管理 state 的一部分:
function visibilityFilter(state = 'SHOW_ALL', action) {
if (action.type === 'SET_VISIBILITY_FILTER') {
return action.filter;
} else {
return state;
}
}
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([{ text: action.text, completed: false }]);
case 'TOGGLE_TODO':
return state.map((todo, index) =>
action.index === index ?
{ text: todo.text, completed: !todo.completed } :
todo
)
default:
return state;
}
}
再开发一个 reducer 调用这两个 reducer,进而来管理整个应用的 state:
function todoApp(state = {}, action) {
return {
todos: todos(state.todos, action),
visibilityFilter: visibilityFilter(state.visibilityFilter, action)
};
}
这差不多就是 Redux 思想的全部。注意到没我们还没有使用任何 Redux 的 API。Redux 里有一些工具来简化这种模式,但是主要的想法是如何根据这些 action 对象来更新 state,而且 90% 的代码都是纯 JavaScript,没用 Redux、Redux API 和其它魔法。
react-redux使用
react-redux原型图:
-
UI组件
- 只负责 UI 的呈现,不带有任何业务逻辑
- 通过props接收数据(一般数据和函数)
- 不使用任何 Redux 的 API
- 一般保存在components文件夹下
-
容器组件
- 负责管理数据和业务逻辑,不负责UI的呈现
- 使用 Redux 的 API
- 一般保存在containers文件夹下
react-redux相关API
- Provider:让所有组件都可以得到state数据
- connect:用于包装 UI 组件生成容器组件
- mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性
- mapDispatchToProps:将分发action的函数转换为UI组件的标签属性
react-redux笔记:
1. 求和案例_react-redux基本使用:
(1).明确两个概念:
1).UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。
2).容器组件:负责和redux通信,将结果交给UI组件。
(2).如何创建一个容器组件————靠react-redux 的 connect函数
connect(mapStateToProps,mapDispatchToProps)(UI组件)
-mapStateToProps:映射状态,返回值是一个对象
-mapDispatchToProps:映射操作状态的方法,返回值是一个对象
(3).备注1:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
(4).备注2:mapDispatchToProps,也可以是一个对象
2. 求和案例_react-redux优化:
(1).容器组件和UI组件整合一个文件
(2).无需自己给容器组件传递store,给<App/>包裹一个<Provider store={store}>即可。
(3).使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。
(4).mapDispatchToProps也可以简单的写成一个对象
(5).一个组件要和redux“打交道”要经过哪几步?
(1).定义好UI组件---不暴露
(2).引入connect生成一个容器组件,并暴露,写法如下:
connect(
state => ({key:value}), //映射状态
{key:xxxxxAction} //映射操作状态的方法
)(UI组件)
(3).在UI组件中通过this.props.xxxxxxx读取和操作状态
container/Count/index.jsx:
import React, { Component } from 'react'
import {
createIncrementAction,
createDecrementAction,
createIncrementAsyncAction
} from '../../redux/count_action'
// 引入connect用于连接UI组件与redux
import { connect } from 'react-redux'
// 定义UI组件
class Count extends Component {
increment = () => {
const { value } = this.selectNumber
this.props.jia(value*1)
}
decrement = () => {
const { value } = this.selectNumber
this.props.jian(value*1)
}
incrementIfOdd = () => {
const { value } = this.selectNumber
if(this.props.count % 2 !== 0) {
this.props.jia(value*1)
}
}
incrementAsync = () => {
const { value } = this.selectNumber
this.props.jisAsync(value*1, 500)
}
render() {
return (
<div>
<h1>当前求和为:{this.props.count}</h1>
<select ref={c => this.selectNumber = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再+</button>
<button onClick={this.incrementAsync}>异步+</button>
</div>
)
}
}
export default connect(
state => ({count: state}),
{
jia: createIncrementAction,
jian: createDecrementAction,
jisAsync: createIncrementAsyncAction
}
)(Count)
3.求和案例_react-redux数据共享版:
(1).定义一个Pserson组件,和Count组件通过redux共享数据。
(2).为Person组件编写:reducer、action,配置constant常量。
(3).重点:Person的reducer和Count的Reducer要使用combineReducers进行合并,
合并后的总状态是一个对象!!!
(4).交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。
4. 求和案例_react-redux开发者工具的使用
(1).yarn add redux-devtools-extension
(2).store中进行配置
import {composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
react-redux最终版:
目录:
src
├── App.jsx
├── containers
│ ├── Count
│ │ └── index.jsx
│ └── Person
│ └── index.jsx
├── index.js
└── redux
├── actions
│ ├── count.js
│ └── person.js
├── reducers
│ ├── count.js
│ ├── index.js
│ └── person.js
├── constant.js
└── store.js
Index.js:
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'
import { Provider } from 'react-redux'
ReactDOM.render(
// 此处需要用Provider包裹App,目的是让App的所有的后代容器组件都能接收到store
<Provider store={store}>
<App/>
</Provider>,
document.getElementById('root')
)
App.jsx:
import React, { Component } from 'react'
import Count from './containers/Count' // 引入的Count的容器组件
import Person from './containers/Person'// 引入的Person的容器组件
export default class App extends Component {
render() {
return (
<div>
<Count/>
<Person />
</div>
)
}
}
redux/actions/count.js:
import { INCREMENT, DECREMENT } from '../constant'
export const increment = data => ({type: INCREMENT, data})
export const decrement = data => ({type: DECREMENT, data})
// 异步action中一般都会调用同步action
export const incrementAsync = (data, time) => {
return (dispatch) => {
setTimeout(() => {
dispatch(increment(data))
}, time)
}
}
redux/actions/person.js:
import { ADD_PERSON } from '../constant'
export const addPerson = personObj => ({type: ADD_PERSON, data: personObj})
redux/reducers/count.js:
import { INCREMENT, DECREMENT } from '../constant'
const initState = 0
export default function countReducer(preState=initState, action) {
const { type, data } = action
switch (type) {
case INCREMENT:
return preState + data
case DECREMENT:
return preState - data
default:
return preState
}
}
redux/reducers/index.js:
// 该文件用于汇总所有的reducer为一个总的reducer
import { combineReducers } from 'redux'
// 引入为Count组件服务的reducer
import count from './count'
// 引入为Person组件服务的reducer
import persons from './person'
// 汇总所有的reducers编程一个总的allReducer
export default combineReducers({
count,
persons
})
redux/reducers/person.js:
import { ADD_PERSON } from '../constant'
// 初始化人的列表
const initState = [{id:'001', name:'tom', age:18}]
export default function personReducer(preState=initState, action) {
const { type,data } = action
switch (type) {
case ADD_PERSON:
return [data, ...preState]
default:
return preState
}
}
redux/constant.js:
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
export const ADD_PERSON = 'add_person'
redux/store.js:
// 引入createStore,专门用于创建redux中最为核心的store对象
import { createStore, applyMiddleware } from 'redux'
import reducer from './reducers'
// 引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
// 进入redux-devtools-extension
import { composeWithDevTools } from 'redux-devtools-extension'
export default createStore(reducer, composeWithDevTools(applyMiddleware(thunk)))