页面截图:
src下目录如下
src
├─actions
│ └─choose-question.tsx
├─assets
│ └─images
├─components
├─reducer
│ └─actionTypes.tsx
│ └─choose-question.tsx
│ └─index.tsx
│ └─store.tsx
├─router
├─css
├─views
│ └─my-paper.tsx
│ └─paper.tsx
│ └─paper2.json // 题目数据
├─App.tsx
└─index.tsx
安装redux
cnpm install --save redux react-redux @types/react-redux @types/redux
- reducer 目录包含所有Redux的reducer
- actions 目录包含所有action构造函数
- components 目录包含所有的傻瓜组件
- containers 目录包含所有的容器组件 (本项目暂时不用)
Redux的3个基本原则:
- 唯一数据源
- 保持状态只读
- 数据改变只能通过纯函数完成
reducer目录下新建一个store.tsx
reducer目录下新建一个store.tsx(这个store.tsx文件也可以不用建,把里面的代码直接放到App组件中),代码如下
import { createStore } from 'redux'
import reducer from './index'
// store 是一个必须包含3个函数 subscribe dispatch getState的对象
let store = createStore(reducer)
export default store
Provider作为根组件提供上下文环境,保证store可以被所有组建访问到
一个应用中,最好只有一个地方需要直接导入Store,这个位置当然应该是在调用最顶层React组件的位置,其余组件应该避免直接导入Store。
该项目中在App.tsx中导入Store后,其余地方就没有再次导入了
在App.tsx中导入Store
App.tsx
import React from 'react'
// 提供包含store的context
import { Provider } from 'react-redux'
import store from './reducer/store'
import ConfigRouter from './router/router' // 自己配置的路由模块
import './App.css'
export default class App extends React.Component {
render () {
return (
)
}
}
选题模块reducer
选题模块reducer(reducer/choose-question.tsx)如下:
import { SELECTED_QUESTION, QUESTION_CATALOG} from './actionTypes'
const initialState = {
selectedQuestion: [], // 选中的题目
questionCatalog: {} // 选中题目的id
}
export const getSelectedQuestion = (state:any) => state.selectedQuestion
export const getQuestionCatalog = (state:any) => state.questionCatalog
const chooseQuestion = (state = initialState, action: any) => {
switch (action.type) {
case SELECTED_QUESTION:
return {
selectedQuestion: [...action.payload],
questionCatalog: getQuestionCatalog(state)
}
case QUESTION_CATALOG:
return {
questionCatalog: {...action.payload},
selectedQuestion: getSelectedQuestion(state)
}
default:
return {
selectedQuestion: getSelectedQuestion(state),
questionCatalog: getQuestionCatalog(state)
}
}
}
export default chooseQuestion
combineReducers组合多个reducer
因为考虑到会有多个模块的reducer,所以reducer/index.tsx文件里使用combineReducers组合reducer
import { combineReducers } from 'redux'
import chooseQuestion from './choose-question' // 选题模块的reducer
// 组合reducer
export default combineReducers({
chooseQuestion
})
调用action里的方法去修改store里的状态
组件内通过调用action里的方法(actions/choose-question.tsx)去修改store里的状态,action/choose-question.tsx
文件定义修改store状态的方法之一如下:
export const addQuestionCatalog = (question: object) => {
return {
type: QUESTION_CATALOG,
payload: question
}
}
需要用到状态管理的组件
- 引入connect
connect方法接收两个参数mapStateToProps和mapDispatchToProps,执行结果依然是一个函数
connect做了两件事
- 把Store上的状态转化为内层傻瓜组件(
Paper组件
)的prop (见下面步骤2) - 把内层傻瓜组件中的用户动作转化为派送给Store的动作(见下面步骤3)
import { connect } from 'react-redux'
- 如果只使用到store里的状态,不去改变store里的状态,定义好state传入组件
// Paper 组件内容省略
const Paper = () => { ... }
const mapStateToProps = (state:any) => {
return {
// combineReducers组合reducer的时候该模块用的是chooseQuestion
// 所以取出来也要用state.chooseQuestion
selectedQuestion: state.chooseQuestion.selectedQuestion,
questionCatalog: state.chooseQuestion.questionCatalog
}
}
export default connect(
mapStateToProps
)(Paper)
- 如果使用到了store里的状态,并且要改变状态,定义好state和dispash方法传入组件
// Paper 组件内容省略
const Paper = () => { ... }
const mapStateToProps = (state:any) => {
return {
selectedQuestion: state.chooseQuestion.selectedQuestion,
questionCatalog: state.chooseQuestion.questionCatalog
}
}
const mapDispatchToProps = (dispatch:any) => {
return {
addSelectQuestion: (question: any) => dispatch(addSelectQuestion(question)),
addQuestionCatalog: (question: any) => dispatch(addQuestionCatalog(question))
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Paper)
项目地址:https://gitee.com/whongli/papers-composing/blob/master/redux-demo