目的:采用状态管理的多种方式:实现一个计数器,可以加一,减一, 置零。
React state
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| import React from "react";
export default class Counter extends React.Component { constructor(props) { super(props); this.state = { value: 0 }; }
handleClick(actions) { switch (actions) { case "INCREASE": return this.setState((state, props) => ({ value: ++state.value })); case "DECREASE": return this.setState((state, props) => ({ value: --state.value })); default: return this.setState({ value: 0 }); } }
render() { return ( <div> <p>{this.state.value}</p> <button onClick={() => this.handleClick("INCREASE")}>+1</button> <button onClick={() => this.handleClick("DECREASE")}>-1</button> <button onClick={() => this.handleClick("RESET")}>0</button> </div> ); } }
|
Flux
Redux
第一步:创建 reducer
- 可以使用单独的一个 reducer,也可以将多个 reducer 合并为一个 reducer,即:combineReducers()
- action 发出命令后将 state 放入 reucer 加工函数中,返回新的 state,对 state 进行加工处理
1 2 3 4 5 6 7 8 9 10
| const reducer = (state = { counter: 0 }, action) => { switch (action.type) { case "INCREASE": return { counter: state.counter + 1 }; case "DECREASE": return { counter: state.counter - 1 }; default: return state; } };
|
第二步:创建 action
- 用户是接触不到 state 的,只能有 view 触发,所以,这个 action 可以理解为指令,需要发出多少动作就有多少指令
- action 是一个对象,必须有一个叫 type 的参数,定义 action 类型
1 2 3 4
| const actions = { increase: () => ({ type: "INCREASE" }), decrease: () => ({ type: "DECREASE" }) };
|
第三步:创建的 store,使用 createStore 方法
- store 可以理解为有多个加工机器的总工厂
- 提供 subscribe,dispatch,getState 这些方法。
1 2 3 4 5 6 7 8
| const store = createStore(reducer);
store.subscribe(() => console.log(store.getState()));
store.dispatch(actions.increase()); store.dispatch(actions.increase()); store.dispatch(actions.increase()); store.dispatch(actions.decrease());
|
具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| import React from "react"; import { createStore } from "redux";
const reducer = (state = { counter: 0 }, action = {}) => { const { type } = action; const { counter } = state; switch (type) { case "INCREASE": return { counter: counter + 1 }; case "DECREASE": return { counter: counter - 1 }; default: return { counter: 0 }; } };
const store = createStore(reducer);
export default class CounterRedux extends React.Component { constructor(props) { super(props); this.state = { counter: 0 }; this.unsubscribe = null; } componentDidMount() { this.unsubscribe = store.subscribe(() => { this.setState({ counter: store.getState().counter }); }); } componentWillUnmount() { this.unsubscribe(); } render() { return ( <div> <h1>{this.state.counter}</h1> <button onClick={() => store.dispatch({ type: "INCREASE" })}>+1</button> <button onClick={() => store.dispatch({ type: "DECREASE" })}>-1</button> <button onClick={() => store.dispatch({ type: "RESET" })}>0</button> </div> ); } }
|
action 可以单独出来:
1 2 3 4 5 6 7
| const actions = { increase: () => ({ type: "INCREASE" }), decrease: () => ({ type: "DECREASE" }), reset: () => ({ type: "RESET" }) };
<button onClick={() => store.dispatch(actions.increase())}>+1</button>;
|
主要是为了展示 redux 的一个工作流程,并没有把状态挂载在最顶层,详细完整版可以参考阮一峰老师的代码:Redux Counter Example。
Redux 的工作流程图,阮一峰博客文章摘录:
React-Redux
Redux 是一款状态管理库,并且提供了 react-redux 库来与 React 亲密配合,这两者的关系如下图:
继续实现计数器,完整 Demo 可以看这里。
src 目录下大体结构:
1 2 3 4 5 6 7 8 9
| ├── actions │ └── counter.jsx ├── components │ └── app.jsx ├── reducers │ └── counter.jsx └── store └── app.jsx ├── index.jsx
|
首先,看入口文件 index.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import React from "react"; import ReactDOM from "react-dom"; import { Provider } from "react-redux"; import { createStore } from "redux";
import reducer from "./reducers/counter.jsx"; import App from "./store/app.jsx";
const store = createStore(reducer);
ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById("root") );
|
Provider 组件,其实就是 Context 实现的,提供一个状态供跨组件使用,只需要把 store 给他传过去,所有的子组件就可以通过 props 属性拿到状态值。
1 2 3 4 5 6 7 8 9 10 11 12
| let Context = React.createContext(); class Provider extends Component { render() { return ( <Context.Provider value={{ store: this.props.store }}> {this.props.children} </Context.Provider> ); } }
|
Reducer 函数,它接受 Action 和当前 State 作为参数,返回一个新的 State,内容和之前的几乎差不多:
1
| import reducer from "./reducers/counter.jsx";
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| export default function reducer(state = { counter: 0 }, action = {}) { const { counter } = state; const { type } = action;
switch (type) { case "INCREASE": return { counter: counter + 1 }; case "DECREASE": return { counter: counter - 1 }; default: return { counter: 0 }; } }
|
React-Redux 的核心之一 connect 方法,用于从 UI 组件生成容器组件。connect 方法接受两个参数:mapStateToProps
和 mapDispatchToProps
。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将 state 映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action。
1
| import App from "./store/app.jsx";
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import { connect } from "react-redux";
import App from "./../components/app.jsx"; import { increaseAction, decreaseAction, resetAction } from "./../actions/counter.jsx";
const mapStateToProps = state => ({ counter: state.counter });
const mapDispatchToProps = dispatch => ({ onIncreaseHandle: () => dispatch(increaseAction), onDecreaseHandle: () => dispatch(decreaseAction), onResetHandle: () => dispatch(resetAction) });
export default connect(mapStateToProps, mapDispatchToProps)(App);
|
Action 的 type 属性:
1 2 3 4
| export const increaseAction = { type: "INCREASE" }; export const decreaseAction = { type: "DECREASE" }; export const resetAction = { type: "RESET" };
|
接着,我们看一下熟悉的 App 组件应该怎么写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import React from "react";
class App extends React.Component { render() { let { counter, onIncreaseHandle, onDecreaseHandle, onResetHandle } = this.props; return ( <div> <h1>{counter}</h1> <button onClick={onIncreaseHandle}>+1</button> <button onClick={onDecreaseHandle}>-1</button> <button onClick={onResetHandle}>0</button> </div> ); } }
export default App;
|
前期做了许多工作,这里如同从父组件里获取 props 属性般获取、触发等行为,所有 store 里的 state 都通过 connect 方法给处理了:
1
| connect(mapStateToProps, mapDispatchToProps)(App);
|
到这里,计数器基本的功能都好了,我的 Demo,阮一峰老师的 Demo,以及讲解的文章,Redux 入门教程(三):React-Redux 的用法,让 react 用起来更得心应手——(react-redux)。
React Hooks
参考资料