React 狀態管理的幾種方式示例

前言

題目:實現一個計數器,能夠加一,減一,置零。主要使用了 State、Redux、React-redux 以及 React-hooks 方式。html

Demo 地址:react

React state

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>
    );
  }
}
複製代碼

Redux

第一步:建立 reducergit

  • 可使用單獨的一個 reducer,也能夠將多個 reducer 合併爲一個 reducer,即:combineReducers()
  • action 發出命令後將 state 放入 reucer 加工函數中,返回新的 state,對 state 進行加工處理
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;
  }
};
複製代碼

第二步:建立 actiongithub

  • 用戶是接觸不到 state 的,只能有 view 觸發,因此,這個 action 能夠理解爲指令,須要發出多少動做就有多少指令
  • action 是一個對象,必須有一個叫 type 的參數,定義 action 類型
const actions = {
  increase: () => ({ type: "INCREASE" }),
  decrease: () => ({ type: "DECREASE" })
};
複製代碼

第三步:建立的 store,使用 createStore 方法shell

  • store 能夠理解爲有多個加工機器的總工廠
  • 提供 subscribe,dispatch,getState 這些方法。
const store = createStore(reducer);

store.subscribe(() => console.log(store.getState()));

store.dispatch(actions.increase()); // {counter: 1}
store.dispatch(actions.increase()); // {counter: 2}
store.dispatch(actions.increase()); // {counter: 3}
store.dispatch(actions.decrease()); // {counter: 2}
複製代碼

具體代碼以下:redux

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 能夠單獨出來:數組

const actions = {
  increase: () => ({ type: "INCREASE" }),
  decrease: () => ({ type: "DECREASE" }),
  reset: () => ({ type: "RESET" })
};
// 觸發
<button onClick={() => store.dispatch(actions.increase())}>+1</button>;
複製代碼

主要是爲了展現 redux 的一個工做流程,並無把狀態掛載在最頂層,詳細完整版能夠參考阮一峯老師的代碼:Redux Counter Examplebash

Redux 的工做流程圖,阮一峯博客文章摘錄:app

Redux-Flow

React-Redux

Redux 是一款狀態管理庫,而且提供了 react-redux 庫來與 React 親密配合。dom

繼續實現計數器,完整 Demo 能夠看這裏

src 目錄下大致結構:

├── actions
│   └── counter.jsx
├── components
│   └── app.jsx
├── reducers
│   └── counter.jsx
└── store
    └── app.jsx
├── index.jsx
複製代碼

首先,看入口文件 index.js:

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 屬性拿到狀態值。

let Context = React.createContext();
class Provider extends Component {
  // 將React-redux中的Provide包裝了react提供的API生成的Context.Provider
  //<Provider store={xxxx}></Provider>,將store掛載在contex上
  render() {
    return (
      <Context.Provider value={{ store: this.props.store }}> {this.props.children} //子組件 </Context.Provider> ); } } 複製代碼

Reducer 函數,它接受 Action 和當前 State 做爲參數,返回一個新的 State,內容和以前的幾乎差很少:

import reducer from "./reducers/counter.jsx";
複製代碼
// ./reducers/counter.jsx
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 方法接受兩個參數:mapStateToPropsmapDispatchToProps。它們定義了 UI 組件的業務邏輯。前者負責輸入邏輯,即將 state 映射到 UI 組件的參數(props),後者負責輸出邏輯,即將用戶對 UI 組件的操做映射成 Action。

import App from "./store/app.jsx";
複製代碼
// ./store/app.jsx
import { connect } from "react-redux";

import App from "./../components/app.jsx";
import {
  increaseAction,
  decreaseAction,
  resetAction
} from "./../actions/counter.jsx";

// mapStateToProps用戶本身定義須要的狀態
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 屬性:

// ./../actions/counter.jsx
export const increaseAction = { type: "INCREASE" };
export const decreaseAction = { type: "DECREASE" };
export const resetAction = { type: "RESET" };
複製代碼

接着,咱們看一下熟悉的 App 組件應該怎麼寫:

// import App from "./../components/app.jsx";
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 方法給處理了:

connect(
  mapStateToProps,
  mapDispatchToProps
)(App);
複製代碼

到這裏,計數器基本的功能都好了,個人 Demo,阮一峯老師的 Demo,以及講解的文章,Redux 入門教程(三):React-Redux 的用法讓 react 用起來更駕輕就熟——(react-redux)

React Hooks

Hook 是 React 16.8 的新增特性。它可讓你在不編寫 class 的狀況下使用 state 以及其餘的 React 特性。

動機:

  • 在組件之間複用狀態邏輯很難
  • 複雜組件變得難以理解
  • 難以理解的 class

React Hooks 的設計目的,就是增強版函數組件,徹底不使用"類",就能寫出一個全功能的組件。

React Hooks 的意思是,組件儘可能寫成純函數,若是須要外部功能和反作用,就用鉤子把外部代碼"鉤"進來。 React Hooks 就是那些鉤子。

import React, { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div> <h1>{count}</h1> <button onClick={() => setCount(count + 1)}>+1</button> <button onClick={() => setCount(count - 1)}>-1</button> <button onClick={() => setCount(0)}>0</button> </div>
  );
}

export default Counter;
複製代碼

對比 Class Component 中將組件狀態放在 state 屬性中維持的作法,React Hook 使用 useState 方法來在 Function Component 中建立狀態變量、建立改變狀態的方法、傳入初始狀態。這樣就實現了一個擁有本身的狀態的 Function Component。

顯而易見,不管是簡潔程度仍是優雅程度,Function Component 都要遠遠優於 Class Component。

Demo 地址:

參考資料

相關文章
相關標籤/搜索