最近学习了 react + redux, 感觉比较迷糊, 为了验证学习成果, 特意写下这篇笔记
redux 的特性
- react 与 redux 没有关系, 是两个独立的技术, 要把两者关联起来, 需要用到 react-redux(react-redux 本身也是独立的技术)
store
- 用 redux.createStore 方法创建 store, 这个 store 可以用于 react-redux 的 Provider
let store = createStore(reducers, "初始值对象")
- store 根据 reducers 创建, [store, reducers] 都应该是唯一的
- store 是所有 reducer 的集合, state 仅是 store 中的数据集合
- Store 有以下职责
- 维持应用的 state;
- 提供 getState() 方法获取 state;
- 提供 dispatch(action) 方法更新 state;
- 通过 subscribe(listener) 注册监听器;
- 通过 subscribe(listener) 返回的函数注销监听器
reducers
- reducers 应该是唯一的, reducers 可以包含多个多层 reducer
- reducers 包含所有被管理的数据(reducer)
reducers = {
reducer1,
reducer2,
reducer3: {
reducer4,
reducer5,
}
}
- reducer 是 store 的最小单位, 一个 reducer 对应一个 变量
- 用 redux.combineReducers 组合多个 reducer, 组合后的对象也是 reducer, 包含了所有 reducer 的才是 reducers
- reducer 就是一个纯函数, 接收旧的 state 和 action, 返回新的 state
// 只要符合要求并能实现功能, reducer 怎么写都可以
// 常用的写法如下
// state 不应该被修改
function reducerName(state = "初始值", action) {
switch(action.type) {
case "事件1":
return state + 1;
case "事件2":
return state - 1;
default:
// 没对应事件 返回 state
return state;
}
}
action
- action 是普通 js 对象, 根据约定俗成保留 type 来表示将要执行的动作, 其它部分没有明确规定
action = {
type: "val1_1__INCREMENT",
val1,
val2,
}
store.dispatch
- store.dispatch(action) 将 action 传到 store
- store.dispatch 会触发所有 reducer, 所以 reducer 的事件名要注意不能重名
store.监听
- store.subscribe 监听 store
- store.subscribe 返回的一个函数 unsubscribe, 执行 unsubscribe 停止监听
unsubscribe = store.subscribe(() =>
// 监听后执行内容
console.log(store.getState())
)
// 停止监听 state 更新
unsubscribe();
react-redux 的特性
- react-redux 的作用就是把 react 和 redux 关联起来
- react-redux 区分[容器组件, 展示组件], 分属不清的称为其它组件
- 容器组件: 描述如何运行(数据获取、状态更新), 在容器组件中声明后, 才能在展示组件中使用
- 展示组件: 描述如何展现(骨架、样式), 差不多就是普通的 react 组件, 多了调用 redux 的变量和方法
- 展示组件可以是函数组件, 也可以是 class组件
- 容器组件根据展示组件生成, 上级调用的是容器组件, 一般容器组件跟展示组件是一对一关系
- 容器组件 = connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])(展示组件)
- mapStateToProps 把当前 Redux store state 映射到展示组件的 props 中, (声明变量)
- mapDispatchToProps 接收 dispatch() 方法并返回期望注入到展示组件的 props 中的回调方法, 声明方法
- mergeProps ???
- options ???
- 通过 react-redux 的 Provider 将 store传入
render(
<Provider store={
store}>
<App />
</Provider>,
document.getElementById("root")
)
- Provider 一般在根组件, 但不一定要用在根组件
demo
import React from "react";
import {
Provider, connect } from "react-redux";
import {
combineReducers, createStore } from "redux";
function val1_1(state = 0, action) {
switch (action.type) {
case "val1_1__INCREMENT":
return state + 1;
case "val1_1__DECREMENT":
return state - 1;
default:
return state;
}
}
function val1_2(state = 0, action) {
switch (action.type) {
case "val1_2__INCREMENT":
return state + 1;
case "val1_2__DECREMENT":
return state - 1;
default:
return state;
}
}
function val2(state = "初始值", action) {
switch (action.type) {
case "val2__RESET":
return action.state
default:
return state
}
}
function val3(state = [], action) {
switch (action.type) {
case "val3__ADD_TODO":
return [
...state,
{
text: action.text,
completed: false
}
]
case "val3__COMPLETE_TODO":
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({
}, todo, {
completed: true
})
}
return todo
})
default:
return state
}
}
let val1 = combineReducers({
val1_1, val1_2 })
let reducers = combineReducers({
val1, val2, val3 })
var store = createStore(reducers)
store.subscribe(() => {
console.log(store.getState())
});
// 展示组件
// connect 没有传入 mapDispatchToProps 将会在 this.props 里得到 dispatch
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
thisState: "this.state",
};
// props.dispatch
}
componentDidMount() {
}
render() {
const {
thisState } = this.state
// 方法和变量都从 this.props 传入
const {
val1,
val1_1,
val1_2,
val2,
val3,
val1_1__INCREMENT,
val1_2__INCREMENT,
val2__RESET,
val3__ADD_TODO
} = this.props
return (
<div>
<button onClick={
val1_1__INCREMENT}>点击触发 val1_1__INCREMENT</button>
<div>val1.val1_1: {
val1.val1_1}</div>
<div>val1_1: {
val1_1}</div>
<button onClick={
val1_2__INCREMENT}>点击触发 val1_2__INCREMENT</button>
<div>val1.val1_2: {
val1.val1_2}</div>
<div>val1_2: {
val1_2}</div>
<button onClick={
val2__RESET.bind(this, "新值")}>点击触发 val2__RESET, 并传入新值</button>
<div>val2: {
val2}</div>
<button onClick={
val3__ADD_TODO}>点击触发 val3__ADD_TODO</button>
<div>新增 val3</div>
{
val3.map((item, index) => {
return <div key={
index}>{
item.text}</div>
})
}
<hr />
<div>本地数据: {
thisState}</div>
{
this.props.children ? <div>{
this.props.children}</div> : null}
</div>
);
}
}
// 声明变量
function mapStateToProps(state, ownProps) {
// ownProps: 传给对应展示组件的原本的 props
// state === store.getState() // true
return {
val1: state.val1,
val1_1: state.val1.val1_1,
val1_2: state.val1.val1_2,
val2: state.val2,
val3: state.val3,
}
}
// 声明方法
function mapDispatchToProps(dispatch, ownProps) {
// ownProps: 传给对应展示组件的原本的 props
return {
val1_1__INCREMENT: () => {
dispatch({
type: "val1_1__INCREMENT",
})
},
val1_2__INCREMENT: () => {
dispatch({
type: "val1_2__INCREMENT",
})
},
val2__RESET: (state) => {
dispatch({
type: "val2__RESET",
state: state
})
},
val3__ADD_TODO: () => {
dispatch({
type: "val3__ADD_TODO",
text: "text"
})
},
}
}
// 对应展示组件最后收到的 props ≈ ownProps + mapStateToProps.return + mapDispatchToProps.return
// mapDispatchToProps 为空的时候, props 会有 dispatch
// 其它组件
// 没有传入 (mapStateToProps, mapDispatchToProps) 称为其它组件
// connect 没有传入 mapDispatchToProps 将会得到 dispatch
const Bpp = (...rest) => {
let dispatch = rest[0].dispatch
return (
<div>
<div onClick={
e => dispatch({
type: "val1_2__INCREMENT" })}>从其它组件修改 val1_2</div>
</div>
);
};
// 容器组件
const ConnectApp = connect(mapStateToProps, mapDispatchToProps)(App)
const ConnectBpp = connect()(Bpp)
const ProviderApp = () => {
return (
<Provider store={
store}>
<ConnectApp>
子组件
<ConnectBpp></ConnectBpp>
</ConnectApp>
</Provider>
);
};
export default ProviderApp
// 根组件调用 ProviderApp 的 demo 就不另外写了
// import ProviderApp from "./components/ProviderApp";
// render((
// <ProviderApp />
// ), document.getElementById("react-container"));
end