redux
是用于数据状态管理,而react
是一个视图层面的库。
如果将两者连接在一起,可以使用官方推荐react-redux
库,其具有高效且灵活的特性。
react-redux
将组件分成:
-
容器组件:存在逻辑处理
-
UI 组件:只负责现显示和交互,内部不处理逻辑,状态由外部控制
通过redux
将整个应用状态存储到store
中,组件可以派发dispatch
行为action
给store
其他组件通过订阅store
中的状态state
来更新自身的视图
React
实际上只是 UI
框架,通过 JSX
生成动态 dom 渲染 UI,没有架构、没有模板、没有设计模式、没有路由、也没有数据管理。所以需要借助其他工具。
redux
npm install redux --save
什么是 redux ?
Redux
是 JavaScript
状态容器,提供可预测化的状态管理。可以理解为全局数据状态管理工具,用来做组件通信等。
为什么使用 redux ?
当没有使用 redux
时兄弟组件间传值将很麻烦,代码很复杂冗余。使用 redux
定义全局单一的数据 Store
,可以自定义 Store
里面存放哪些数据,整个数据结构也是自己清楚的。
redux 工作流 ?
- store:推送数据的仓库
- reducer:帮助 store 处理数据的方法(初始化、修改、删除)
- actions:数据更新的指令
- react 组件(UI):订阅 store 中的数据
redux 用法:
import { createStore } from 'redux'
/*
* 这是一个 reducer,形式为 (state, action) => state 的纯函数。描述了 action 如何把 state 转变成下一个 state。
* state 的形式取决于你,可以是基本类型、数组、对象、甚至是 Immutable.js 生成的数据结构。
* 当 state 变化时需要返回全新的对象,而不是修改传入的参数。
*/
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state;
}
}
// 创建 Redux store 来存放应用的状态
// API 是 { subscribe, dispatch, getState }
const store = createStore(counter);
// 可以手动订阅更新,也可以事件绑定到视图层。
store.subscribe(() =>
const sotreState = store.getState()
......
)
// 改变内部 state 惟一方法是 dispatch 一个 action。
store.dispatch({ type: 'INCREMENT' })
store.dispatch({ type: 'DECREMENT' })
redux 三大原则:
单一数据源:整个应用的 state 存放在唯一的一个 store 中。store.getState()
state 是只读的,唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
store.dispatch({
type: 'COMPLETE_TODO',
index: 1
})
使用纯函数来执行修改(reducer:接收先前的 state 和 action,并返回新的 state)。
function visibilityFilter(state = 'SHOW_ALL', action) {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return action.filter
default:
return state
}
}
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
text: action.text,
completed: false
}
]
case 'COMPLETE_TODO':
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: true
})
}
return todo
})
default:
return state
}
}
import { combineReducers, createStore } from 'redux'
const reducer = combineReducers({ visibilityFilter, todos })
const store = createStore(reducer)
React-Redux
npm install react-redux --save
React-Redux
是 Redux
的官方 React
绑定库。它能够使你的 React
组件从 Redux store
中读取数据,并且向 store
分发 actions
以更新数据
React-Redux
将所有组件分成两大类:UI
组件和容器组件。UI
组件负责 UI
的呈现,容器组件负责管理数据和逻辑。
UI
组件:只负责 UI 的呈现,不带有任何业务逻辑;没有状态(即不使用this.state
这个变量);所有数据都由参数this.props
提供;不使用任何Redux
的API
- 容器组件:负责管理数据和业务逻辑,不负责
UI
的呈现;带有内部状态;使用Redux
的API
。
React-Redux
规定,所有的 UI
组件都由用户提供,容器组件则是由 React-Redux
自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它。
connect()
import { connect } from 'react-redux'
const VisibleTodoList = connect(mapStateToProps, mapDispatchToProps)(TodoList)
上面 VisibleTodoList
便是 UI
组件 TodoList
通过 connect
方法自动生成的容器组件。
connect
方法接受两个参数:mapStateToProps
和 mapDispatchToProps
。它们定义了 UI
组件的业务逻辑。前者负责输入逻辑,即将 state
映射到 UI
组件的参数 props
,后者负责输出逻辑,即将用户对 UI
组件的操作映射成 Action
。
mapStateToProps()
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
mapStateToProps
是一个函数,它接受 state
作为参数,返回一个对象。这个对象有一个 todos
属性,代表 UI
组件的同名参数,后面的 getVisibleTodos
也是一个函数,可以从 state
算出 todos
的值。
mapStateToProps
建立一个从(外部的)state
对象到(UI
组件的)props
对象的映射关系。执行后应该返回一个对象,里面的每一个键值对就是一个映射。
mapDispatchToProps()
mapDispatchToProps
用来建立 UI
组件的参数到 store.dispatch
方法的映射。它定义了哪些用户的操作应该当作 Action
,传给 Store
。它可以是一个函数,也可以是一个对象。
是函数则会得到 dispatch
和 ownProps
(容器组件的 props
对象)两个参数。
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: () => {
dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: ownProps.filter,
})
}
}
}
是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。
const mapDispatchToProps = {
onClick: (filter) => {
type: 'SET_VISIBILITY_FILTER',
filter: filter
};
}
<Provider> 组件
connect 方法生成容器组件以后,需要让容器组件拿到 state 对象,才能生成 UI 组件的参数。
React-Redux 提供 Provider 组件,使整个 app 访问到 Redux store 中的数据 即state。
// src/index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import reportWebVitals from './reportWebVitals'
import { Provider } from 'react-redux'
import store from './redux/store'
import App from './App'
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
)
reportWebVitals()