案例:
点击按钮,数字+1,初始值100
单用户模块化拆分
src/store/action.js
src/store/index.js
src/store/reducer.js
src/store/mutation.js
小分析:
代码
index.js
import React from 'react';
import ReactDOM from 'react-dom';
// 根组件
import App from './App';
import store from './store'
// 定义本项目的路由模式
// BrowserRouter history模式
// HashRouter hash模式
import { BrowserRouter as Router } from 'react-router-dom'
ReactDOM.render(
<Router>
<App />
</Router>
document.getElementById('root')
);
App.js
import React, { Component } from 'react';
// 引入redux的数据仓库
import store from './store'
import { incrAction } from './store/action'
class App extends Component {
// 只执行一次
state = {
title: '标题',
// store.getState() 返回的是state对象数据
...store.getState()
}
unSubscribe = null
componentDidMount() {
// 订阅redux中的数据是否有更改,如果有,则需要通知当前组件更新一次
// this.setState({key:value})
// subscribe方法返回一个函数,用来取消订阅所用
this.unSubscribe = store.subscribe(() => this.setState(store.getState()))
// console.log(unSubscribe);
// store.subscribe(() => this.setState({}))
}
componentWillUnmount() {
// 取消订阅
this.unSubscribe && this.unSubscribe()
}
addNum = () => {
// 通知redux中state对象中的属性count要累加对应的数值
store.dispatch(incrAction(10))
}
render() {
let { count } = this.state
// let { count } = store.getState()
return (
<div>
<h3>{count}</h3>
<button onClick={this.addNum}>累加数据</button>
</div>
);
}
}
export default App;
src/store/action.js
export const incrAction = (n = 1) => ({
type: 'incr',
payload: n
})
src/store/index.js
// 创建一个数据仓库
import { createStore } from 'redux'
// redux调试工具
import { composeWithDevTools } from 'redux-devtools-extension';
import reducer from './reducer';
// 导出数据仓库
export default createStore(
reducer,
// window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
composeWithDevTools()
)
src/store/reducer.js
import mutation from "./mutation"
// 数据reducer时,一定要给state有一个初始数据,此数据就是redux中统一数据源
const initState = {
count: 100
}
// state只能通过reducer纯函数来修改,不是直接修改state数据,返回一个新的state
const reducer = (state = initState, { type, payload }) => {
if (mutation[type]) {
// 第1次它要初始化,它有一个默认的 dispatch操作@@INIT动作,
// 它无需开发者去处理,所以要判断,去除
// 当前的操作它存在
return mutation[type](state, payload)
}
return state
}
export default reducer
src/store/mutation.js
// mutation操作
const mutation = {
incr(state, data) {
return {
...state,
count: state.count + data
}
}
}
export default mutation
react-redux
npm i -S react-redux redux
src/index.js
装饰器的写法:
下面是分析过程:
react-redux 拆分
使用异步中间件 redux-thunk
code 图示版
多人开发 模块化拆分
code
index.js
import React from 'react';
import ReactDOM from 'react-dom';
// 根组件
import App from './App';
import { BrowserRouter as Router } from 'react-router-dom'
// redux
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<Router>
<App />
</Router>
</Provider>,
document.getElementById('root')
);
App.js
import React, { Component } from 'react';
import Films from './components/Films';
class App extends Component {
render() {
return (
<div>
<Films />
</div>
);
}
}
export default App;
src\components\Films\index.jsx
import React, { Component } from 'react'
import connect from './connect';
@connect
class Films extends Component {
componentDidMount() {
this.props.addFilmAction()
}
render() {
return (
<div>
<ul>
{
this.props.films.map(item => (
<li key={item.filmId}>{item.name}</li>
))
}
</ul>
</div>
);
}
}
export default Films;
src\store\index.js
import { createStore, applyMiddleware, combineReducers } from 'redux'
// combineReducers 它可以把多个reducer函数合并为一个大的reducer函数
// 引入中间件
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
// 引入拆分后的模块的reducer
import count from '@/reducer/count'
import film from '@/reducer/film'
// 把拆分后的小的reducer合并成为一个大的reducer
const reducer = combineReducers({
// key:value
// key的作用为:state的命名空间名称,在redux中只有state有命名空间限制,所以在redux中的type操作名称最好不要重名,重名它会多次执行
// value就是小的redux纯函数
count,
film
})
export default createStore(
reducer,
composeWithDevTools(applyMiddleware(thunk))
)
src\reducer\count.js
import * as mutation from '@/mutation/countMutation'
const initState = {
num: 100
}
const reducer = (state = initState, { type, data }) => {
if (mutation[type]) {
return mutation[type](state, data)
}
return state
}
export default reducer
src\reducer\film.js
import * as mutation from '@/mutation/filmMutation'
const initState = {
films: []
}
const reducer = (state = initState, { type, data }) => {
if (mutation[type]) {
return mutation[type](state, data)
}
return state
}
export default reducer
src\mutation\countMutation.js
export const addNum = (state, data) => ({
...state,
num: state.num + data
})
src\mutation\filmMutation.js
export const addFilm = (state, data) => ({
...state,
films: [...state.films, ...data]
})
src\utils\http.js
import axios from 'axios'
// 得到一个新的请求实例
const instance = axios.create({
// 请求超时时间
timeout: 10000
});
// 响应拦截器
instance.interceptors.response.use(res => res.data, err => Promise.reject(err))
// get请求
export const get = (url, config = {}) => instance.get(url, config)
// post请求
export const post = (url, data = {}, config = {}) => instance.get(url, data, config)
export const put = (url, data = {}, config = {}) => instance.put(url, data, config)
export const del = (url, data = {}, config = {}) => instance.delete(url, data, config)
src\api\config\filmConfig.js
const config = {
film: '/api/v1/getNowPlayingFilmList?cityId=110100&pageNum=1&pageSize=10'
}
export default config
src\api\filmApi.js
import { get } from '../utils/http'
import config from './config/filmConfig'
// 电影列表
export const getFilmsApi = () => get(config.film)
src\action\filmAction.js
import { getFilmsApi } from '@/api/filmApi'
/* // 对象中的方法,在没有使用中间件之前中能返回一个json对象,现在使用了中间件可以返回一个函数
export addFilm() {
return async function (dispatch) {
let ret = await getFilmsApi()
dispatch({
type: 'addFilm',
data: ret.data.films
})
}
} */
// 对象中的方法,在没有使用中间件之前中能返回一个json对象,现在使用了中间件可以返回一个函数
export const addFilmAction = () => async dispatch => {
let ret = await getFilmsApi()
dispatch({
type: 'addFilm',
data: ret.data.films
})
}
src\components\Films\connect.js
import { connect } from 'react-redux'
// 全部导入,它得到的是一个对象
import * as actions from '@/action/filmAction'
export default connect(state => {
// console.log(state);
// 使用了redux模块化,所以在获取state中的数据时要有命名空间属性
return state.film
}, actions)
redux模块化自动导入