第一节:状态管理概述
什么是状态管理?
状态管理是指在应用中管理和更新组件数据的过程。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);