8-1~8-6 RN之状态管理Dva

149 阅读3分钟

第一节:状态管理概述

什么是状态管理?

状态管理是指在应用中管理和更新组件数据的过程。React 本身是一个用于构建用户界面的库,不提供状态管理功能。当应用涉及多个页面或组件时,需要处理数据传递、视图更新和异步逻辑等问题。这些功能通常由状态管理库来实现。

常见的状态管理库包括 Redux、Rematch、Dva 和 MobX 等。Redux 和 MobX 是两种不同的状态管理方案:

  • Redux:通过单一的存储仓库管理状态,所有操作必须通过 reducer 和 action 完成,强调不可变数据。
  • MobX:通过多个存储仓库管理状态,允许直接操作数据,监听数据变化并自动更新视图。

Dva 是基于 Redux 和 Redux-Saga 的轻量级框架,封装了 Redux 的复杂性,提供了更简洁的 API 和规范化的代码结构。

示例:使用 Redux 和 Redux-Saga 实现登录功能

1. 创建项目

mkdir redux-demo && cd redux-demo
npm init -y
yarn add redux react-redux redux-saga
yarn add -D webpack webpack-cli

2. 文件结构

src/
  index.js          // 入口文件
  render.js         // 渲染函数
  reducers.js       // Redux reducer
  effects.js        // Redux-Saga effects

3. 实现代码

reducers.js

const defaultState = { username: '' };

function loginReducer(state = defaultState, action) {
  switch (action.type) {
    case 'login':
      return { username: 'chen' };
    case 'logout':
      return { username: '' };
    default:
      return state;
  }
}

export default loginReducer;

effects.js

import { call, put, takeEvery } from 'redux-saga/effects';

function mockAjax(method, url, data) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(data), 1000);
  });
}

function* fetchLogin(action) {
  const user = yield call(mockAjax, 'POST', '/api/login', { userName: '李四' });
  yield put({ type: 'login', user });
}

function* mySaga() {
  yield takeEvery('fetchLogin', fetchLogin);
}

export default mySaga;

index.js

import { createStore, applyMiddleware } from 'redux';
import loginReducer from './reducers';
import { createSagaMiddleware } from 'redux-saga';
import mySaga from './effects';
import render from './render';

const sagaMiddleware = createSagaMiddleware();
const store = createStore(loginReducer, applyMiddleware(sagaMiddleware));

sagaMiddleware.run(mySaga);

render({ state: store.getState(), dispatch: store.dispatch });

store.subscribe(() => {
  render({ state: store.getState(), dispatch: store.dispatch });
});

render.js

function render({ state, dispatch }) {
  const root = document.getElementById('root');
  root.innerHTML = '';

  const element = document.createElement('div');
  const button = document.createElement('button');
  const span = document.createElement('span');

  span.innerHTML = '用户名:';
  button.addEventListener('click', () => {
    if (state.username) {
      dispatch({ type: 'logout' });
    } else {
      dispatch({ type: 'fetchLogin' });
    }
  });

  if (state.username) {
    button.innerHTML = '登出';
    span.innerHTML += state.username;
  } else {
    button.innerHTML = '登录';
  }

  element.appendChild(span);
  element.appendChild(button);
  root.appendChild(element);
}

export default render;

4. 运行项目

npx webpack

通过浏览器访问 dist/index.html,可以看到登录/登出按钮,点击后会更新用户名。

Dva 的优势

Dva 是基于 Redux 和 Redux-Saga 的轻量级框架,封装了 Redux 的复杂性,提供了更简洁的 API 和规范化的代码结构。Dva 的特点包括:

  • 数据仓库:所有状态集中管理,组件通过绑定数据仓库获取状态。
  • Model 模块化:状态和逻辑分离,便于维护和扩展。
  • 异步支持:内置 Redux-Saga,支持异步操作。

在 React Native 项目中,由于 React Router 不支持 React Native,因此需要使用 Dva 的核心包 dva-core

第二节:集成 Dva-Core

安装依赖

yarn add dva-core-ts react-redux @types/react-redux

配置 Dva

config/dva.ts 中创建 Dva 实例:

import { create } from 'dva-core-ts';

const app = create();

// 装载 Model
import { models } from '@/models';
models.forEach((model) => app.model(model));

// 初始化 Dva
app.start();

export const store = app._store;

定义 Model

models/home.ts 中定义 Model:

import { Model, Effect, Reducer } from 'dva-core-ts';

interface HomeState {
  num: number;
}

interface HomeModel extends Model {
  namespace: 'home';
  state: HomeState;
  reducers: {
    add: Reducer<HomeState>;
  };
  effects: {
    asyncAdd: Effect;
  };
}

const initialState: HomeState = { num: 0 };

const homeModel: HomeModel = {
  namespace: 'home',
  state: initialState,
  reducers: {
    add(state = initialState, { payload }) {
      return {
        ...state,
        num: payload ? state.num + payload.num : state.num + 1,
      };
    },
  },
  effects: {
    *asyncAdd({ payload }, { put }) {
      yield put({ type: 'add', payload: { num: 1 } });
    },
  },
};

export default homeModel;

models/index.ts 中收集所有 Model:

import home from './home';

export const models = [home];

export type RootState = {
  home: typeof home.state;
};

使用 Dva

src/index.tsx 中使用 Dva:

import React from 'react';
import { Provider } from 'react-redux';
import Navigator from '@/navigator/index';
import store from '@/config/dva';

export default class App extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <Navigator />
      </Provider>
    );
  }
}

在页面组件中使用 Dva:

import { connect } from 'react-redux';

const mapStateToProps = ({ home }: RootState) => ({
  num: home.num,
});

const connector = connect(mapStateToProps);

type ModelState = ReturnType<typeof mapStateToProps>;

interface IProps extends ModelState {
  navigation: NavigationScreenProp<NavigationState>;
}

class Home extends React.Component<IProps> {
  handleAdd = () => {
    const { dispatch } = this.props;
    dispatch({ type: 'home/asyncAdd' });
  };

  render() {
    const { num } = this.props;
    return (
      <View>
        <Text>{num}</Text>
        <Button title="加一" onPress={this.handleAdd} />
      </View>
    );
  }
}

export default connector(Home);