【汽车之家】React状态管理实践

513 阅读4分钟

引言

React作为一个数据驱动的前端框架,状态管理就显得尤为重要。本文旨在对开发实践过程中的实现进行记录及总结。

一、组件内状态管理

  • React state

通过 state 管理状态,使用 setState 更新状态,从而更新UI

export default class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 0,
      otherValue: 0
    };
  }

  handleAdd() {
    this.setState((state, props) => ({
    ...state,
      value: ++state.value
    }));
  }

  render() {
    return (
      <div>
        <p>{this.state.value}</p>
        <button onClick={() => this.handleAdd}>Add</button>
      </div>
    );
  }
}
复制代码

react自身的状态管理方案缺点:即使只更新state中某个值,也需要带上其他值(otherValue),操作繁琐

  • React Hooks

React16.8中正式增加了hooks。通过hooks管理组件内状态,简单易用可扩展。

动机:

在组件之间复用状态逻辑很难

复杂组件变得难以理解

难以理解的 class

React Hooks 的设计目的,就是加强版函数组件,完全不使用"类"来定义组件,就能写出一个全功能的组件。

import React, { useState } from "react";

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

export default Counter;
复制代码

对比 Class 组件中,使用react自身的状态管理方法,React Hooks使用useState方法在 Function 组件中创建状态变量、更新状态的方法、赋值初始状态。这样就实现了一个拥有自己的状态的 Function 组件。更加的简介优雅。

在组件中执行副作用操作时(数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用),Function 组件使用Effect Hook 与 Class组件对比将更明显,有兴趣的同学可查看 官网Demo。以及更多的Hook API

二、跨组件状态管理

当项目业务需求复杂度的不断增加,我们一定会遇到以下问题:

1、如何实现跨组件通信、状态同步以及状态共享?

我们可以使用React提供的context API:

function useCounter() {
  let [count, setCount] = useState(0)
  let increment = () => setCount(count + 1)
  return { count, increment }
}

let Counter = createContext(null)

function CounterDisplay() {
  let counter = useContext(Counter)
  return (
    <div>
      <p>You clicked {counter.count} times</p>
      <button onClick={counter.increment}>Add</button>
    </div>
  )
}

function App() {
  let counter = useCounter()
  return (
    <Counter.Provider value={counter}>
      <CounterDisplay />
    </Counter.Provider>
  )
}
复制代码

但是context只是将状态提升至共有的父组件来实现,看似跨组件,实则还是逐级传递来实现组件间通信、状态同步以及状态共享。另外,使用context会使得组件的服用性变差。

2、如何避免组件臃肿?

当组件的业务逻辑非常复杂时,我们会发现代码越写越多,因为我们只能在组件内部去控制数据流,没办法抽离,Model和View都放在了View层,整个组件显得臃肿不堪,难以维护。

3,如何让状态变得可预知,甚至可回溯? 当数据流混乱时,我们一个执行动作可能会触发一系列的setState,我们如何能够让整个数据流变得可“监控”,甚至可以更细致地去控制每一步数据或状态的变更?

4,如何处理异步数据流? react自身并未提供多种处理异步数据流管理的方案,仅用一个setState已经很难满足一些复杂的异步流场景; 这个时候,我们就需要一个专门的状态管理工具!

  • React-Redux

Redux 是一款状态管理库,并且提供了 react-redux 库来与 React 亲密配合。

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";

import App from "app.jsx";
<!--创建 reducer-->
const reducer = (state = { counter: 0 }, action = {}) => {
  const { counter } = state;
  const { type } = action;
    switch (type) {
      case 'INCREASE':
         return { counter: counter + 1 };
      default:
        return state;
  }
}

<!--创建 store-->
const store = createStore(reducer);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);
复制代码

Provider 组件,其实就是 Context 实现的,提供一个状态供跨组件使用,只需要把 store 给它传过去,所有的子组件就可以通过 props 属性拿到状态值。

app.jsx

import React from "react";

<!--创建 action-->
const increaseAction = { type: "INCREASE" };

const mapStateToProps = state => ({ counter: state.counter });

const mapDispatchToProps = dispatch => ({
  onIncreaseHandle: () => dispatch(increaseAction)
});
class App extends React.Component {
  render() {
    let {
      counter,
      onIncreaseHandle
    } = this.props;
    return (
      <div>
        <h1>{counter}</h1>
        <button onClick={onIncreaseHandle}>+1</button>
      </div>
    );
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(App);
复制代码

React-Redux 的核心方法之一 connect,用于从 UI 组件生成容器组件。connect 方法接受两个参数:

mapStateToProps:建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。

mapDispatchToProps:将用户对 UI 组件的操作映射成 Action。

react-redux 7.1的hooks版本提供了useSelector()、useDispatch()、useStore()这3个主要方法,分别对应与mapState、mapDispatch以及直接拿到redux中store的实例。有兴趣的同学可查看

总结

在React中,状态管理分为全局状态管理和局部状态管理,React Hooks简化了局部状态管理,使组件逻辑清晰,渲染方便。但它并不能解决组件间通信问题,而React-Redux正是用来解决该问题。也就是说,React-Redux等状态管理和React Hooks本质上并不矛盾。相反,搭配使用让项目更健壮(如果你需要React-Redux的话)。

作者:汽车之家-前端体验部-朱富贵