内容简介:简单概述:react-readux中 通过
redux 运行流程图:
简单概述: click -> store.dispatch(action) -> reduer -> newState -> viewUpdate
react-readux中 通过 connect 链接组件和 redux , **this.props.dispatch()**调用
后面将会讲到...
redux 依赖包也是十分的简洁
demo
const redux = require('redux')
const createStore = redux.createStore
const types = {
UPDATE_NAME: 'UPDATE_NAME'
}
const defaultStore = {
user: 'tom'
}
/**
* reducer 纯函数 接收一个state,返回一个新的state
* @param {Object} state
* @param {Object} action [type] 必选参数
* @return newState
* */
function getUser(state = defaultStore, action) {
const { type, payload } = action
let res = Object.assign({}, defaultStore)
switch (type) {
case types.UPDATE_NAME:
res.user = payload.name
break
default:
return res
}
return res
}
const store = createStore(getUser)
/**
* listener
* */
store.subscribe(() => {
console.log(store.getState())
})
/**
* dispatch(action) action
* */
store.dispatch({
type: types.UPDATE_NAME,
payload: {
name: '大帅哥'
}
})
//@log { name: '大帅哥' }
复制代码
- 用户发出
action【store.dispatch(action)】 -
Store自动调用Reducer, 返回新的state【let nextState = getUser(previousState, action)】 -
State一旦有变化,Store就会调用监听函数 【store.subscribe(listener)】
运行过程如下:
store
Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store 常用方法:
- store.dispatch() :分发 action 较为常用
- store.subscribe() : state 发生变化后立即执行
- store.getState() : 获取store 中存着的state
createStore
createStore 如其名,创建 store 下面是该方法的部分源码:
/**
* @param {Function} reducer 函数
* @param {any} [preloadedState] The initial state
* @param {Function} [enhancer] The store enhancer
* @returns {Store}
* */
export default function createStore(reducer, preloadedState, enhancer) {
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
// ...
return {
dispatch, // 分发 action
subscribe, // 监听器
getState, // 获取 store 的 state 值
replaceReducer,
[$$observable]: observable // 供Redux内部使用
}
}
复制代码
-
preloadedState: 初始化的initialState,第二个参数不是Object,而是Function,createStore会认为你忽略了preloadedState而传入了一个enhancer -
createStore会返回enhancer(createStore)(reducer, preloadedState)的调用结果,这是常见高阶函数的调用方式。在这个调用中enhancer接受createStore作为参数,对createStore的能力进行增强,并返回增强后的createStore
dispatch(action)
diapatch 是store对象的方法,主要用来分发 action ,
redux规定action一定要包含一个type属性,且type属性也要唯一
dispatch 是 store 非常核心的一个方法,也是我们在应用中最常使用的方法,下面是dispatch的源码 :
function dispatch(action) {
if (!isPlainObject(action)){ // 校验了action是否为一个原生js对象
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
if (typeof action.type === 'undefined') { // action对象是否包含了必要的type字段
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
if (isDispatching) {// 判断当前是否处于某个action分发过程中, 主要是为了避免在reducer中分发action
throw new Error('Reducers may not dispatch actions.')
}
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
// 在一系列检查完毕后,若均没有问题,将当前的状态和action传给当前reducer,用于生成新的state
return action
}
复制代码
reducer && store.replaceReducer
Redux中负责响应action并修改数据的角色就是 reducer , reducer 的本质实际上是一个函数 replaceReducer:
/**
* @desc 替换当前的reducer的函数
* @param {Function}
* @return {void}
*/
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}
currentReducer = nextReducer
dispatch({ type: ActionTypes.REPLACE })
}
复制代码
replaceReducer 使用场景:
- 当你的程序要进行代码分割的时候
- 当你要动态的加载不同的reducer的时候
- 当你要实现一个实时reloading机制的时候
中间件 middleware
以上介绍了redux的实现流的过程,应用场景无非于
button -- click --> disptch -- action --> reducer -- newState --> view
但是这种实现方式是基于同步的方式的,日常开发中当然少不了 http 这些异步请求,这种情况下必须等到服务器数据返回后才重新渲染 view, 显然某些时候回阻塞页面的展示。
举例来说,要添加日志功能,把 Action 和 State 打印出来,可以对store.dispatch进行如下改造。
let next = store.dispatch;
store.dispatch = function dispatchAndLog(action) {
console.log('dispatching', action);
next(action);
console.log('next state', store.getState());
}
复制代码
上面代码中,对store.dispatch进行了重定义,在发送 Action 前后添加了打印功能。这就是中间件的雏形。
中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。
applyMiddleware
Redux提供了 applyMiddleware 来装载 middleware : 它是 Redux 的原生方法,**作用是将所有中间件组成一个数组,依次执行。**下面是它的源码。
/**
* @param {...Function} middlewares
* returns {Function} A store enhancer applying the middleware
*/
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
复制代码
所有中间件被放进了一个数组chain,然后嵌套执行,最后执行store.dispatch。可以看到,中间件内部(middlewareAPI)可以拿到 getState 和 dispatch 这两个方法
compose 实际上是函数式编程中的组合,接收多个函数体并且将其组合成一个新的函数,例如 compose 后 [fn1, fn2...] 依次从右到左嵌套执行函数 而 compose 用于 applyMiddleware 也是为了组合中间件 dispatch = compose(...chain)(store.dispatch) ==> dispatch=fn1(fn2(fn3(store.dispatch)))
/**
* @param {...Function} funcs The functions to compose.
* @returns {Function} A function obtained by composing the argument functions
*/
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
复制代码
redux-thunk
上面的中间件的介绍可以知道 redux 通过 applyMiddleware 来装载中间件,通过compose 方法可以组合函数
异步的问题可以通过 redux-thunk 解决,用法也不难 react 组件中使用相关如下:
// 配置 redux 加上这个...
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
// ...
const store = createStore(getUser, compose(
applyMiddleware(thunk)
))
// react 中使用
import { connect } from 'react-redux'
handleClick = () => {
this.props.dispatch(
dispatch => {
return axios.get('https://randomuser.me/api/').then(
res => {
dispatch({
type: types.CHANGE_ARRAY,
payload: {
name: res.data.results[0].name.title
}
})
}
)
}
)
}
const mapStateToProps = (state, props) => {
return {
name: state.demo.name
}
}
export default connect(
mapStateToProps
)(Demo)
复制代码
处理异步的还有很多插件 如 redux-soga 等,楼主并未实践过,所以不做延伸...
react-redux
下面是在react中使用的代码的雏形:
import { createStore } from 'redux'
let defaultState = {
count: 1
}
/**
* Reducer
* */
function demoReducer(state = defaultState, action = {}) {
const { type, payload } = action
const res = Object.assign({}, state)
if (type === 'changeCount') {
res.count = payload.count
}
return res
}
/**
* @Store 存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。
* combineReducers({ ...reducers }) 可以组合多个reducer
* */
const store = createStore(
demoReducer,
window.devToolsExtension && window.devToolsExtension() // 配置redux 开发工具
)
// ... 根元素下配置下 Provider
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById('root')
)
// 组件中使用
import { connect } from 'react-redux'
//use
this.dispatch({
type: 'changeCount',
payload: {
count: 22
}
})
const mapStateToProps = (state, props) => {
return {
name: state.demo.name
}
}
export default connect(
mapStateToProps
)(Demo)
复制代码
mapStateToProps
mapStateToProps
mapDispatchToProps
mapDispatchToProps mapDispatchToProps
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
// 页面中使用...
this.props.changeName()
const mapDispatchToProps = { changeName } = (dispatch, props) => {
return bindActionCreators({
changeName: function() {
return {
type: types.UPDATE_NAME,
payload: {
name: '大大大'
}
}
}
}, dispatch)
}
export default connect(
mapDispatchToProps
)(App)
复制代码
模块化配置
下面的配置仅供参考。
安装
npm install redux react-redux redux-thunk --save npm install immutability-helper --save npm install babel-plugin-transform-decorators-legacy -D 复制代码
当state中存放的字段内存未发生改变时,视图并不会更新。所以借用到了 immutability-helper ,辅助更新 当然也可以替换掉json数组,这样便会触发视图的更新了。
src 下新建 store 文件,如下所示:
配置文件
types.js 文件
export const UPDATE_NAME = 'UPDATE_NAME' export const UPODATE_ARRAY = 'UPODATE_ARRAY' 复制代码
demoModule.js 文件
import * as types from '../types'
import update from 'immutability-helper'
const defaultState = {
name: '默认值',
jsonArray: [{ name: '数组默认值' }],
array: []
}
/**
* demo reducer
* */
export default function demo(state = defaultState, action = {}) {
const { type, payload } = action
const res = Object.assign({}, state)
switch (type) {
case types.UPDATE_NAME:
res.name = payload.name
break
case types.UPODATE_ARRAY:
res.jsonArray = update(state.jsonArray, {
[0]: {
$set: { name: payload.name }
}
})
break
default:
}
return res
}
复制代码
modeules 文件夹下的 index.js (模块入口文件)
import { combineReducers } from 'redux'
import demo from './demoModule'
const Reducers = combineReducers({
demo
})
export default Reducers
复制代码
combineReducers : 用于合并 reducers
store 文件下的 index ( redux 总入口文件)
import { createStore, applyMiddleware, compose } from 'redux'
import Modules from './modules'
import thunk from 'redux-thunk'
const store = createStore(Modules, compose(
applyMiddleware(thunk),
window.devToolsExtension && window.devToolsExtension() // 配置redux 开发工具
));
export default store
复制代码
-
Redux默认只处理同步,异步任务需要redux-thunk中间件 - 引入
applyMiddleware来处理中间件 - 使用
compose结合thunk和window.devToolsExtension
修改src 下 index.js (渲染根组件的入口文件)
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root'))
复制代码
测试: demo.jsx 组件
import React, { Component } from 'react'
import { connect } from 'react-redux'
import * as types from './types'
import axios from 'axios'
import { Button } from 'antd'
/**
* @state store 中的state
* @props 组件内接收的props 写了第二个参数props,那么当组件内props发生变化的时候,mapStateToProps也会被调用
* @return Object
* */
const mapStateToProps = (state, props) => {
return {
name: state.demo.name,
jsonArray: state.demo.jsonArray,
array: state.demo.array,
}
}
@connect(mapStateToProps)
class App extends Component {
constructor(props) {
super(props)
this.state = {}
}
handleClick = (type) => {
switch (type) {
case 1:
// 常规调用
this.props.dispatch({
type: types.UPDATE_NAME,
payload: {
name: '同步调用redux'
}
})
break
case 2:
// 异步请求
this.props.dispatch(
dispatch => {
return axios.get('https://randomuser.me/api/').then(
res => {
dispatch({
type: types.UPDATE_NAME,
payload: {
name: res.data.results[0].name.title
}
})
}
)
}
)
break
case 3:
// 改变json数组,通过普通方式
this.props.dispatch({
type: types.UPODATE_ARRAY,
payload: {
name: '这是需要被渲染的 name'
}
})
break
default:
}
}
componentWillReceiveProps() {
console.log('props change')
}
render() {
const { name, jsonArray } = this.props
return (
<div>
name: {name} <br/>
jsonArray[0]['name'] : {jsonArray[0]['name']}<br/>
<Button onClick={e => this.handleClick(1)}>常规调用</Button>
<Button onClick={e => this.handleClick(2)}>异步调用</Button>
<Button onClick={e => this.handleClick(3)}>改变json数组</Button>
</div>
)
}
}
export default App
复制代码
上面用了装饰器模式来 connect , 配置一下 babel 就好了
"plugins": [ "transform-decorators-legacy" ] 复制代码
以上配置,仅供参考,网上有更多更好的,也求大家推荐一下更好地配置。这是一篇学习记录,学海无涯,希望自己加油...
参考
- 阮一峰 redux 入门教程
- 配置方案相关文件 store 文件夹
以上所述就是小编给大家介绍的《react-redux 学习以及模块化配置方案》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Android模块化改造以及模块化通信框架
- Laravel 模块化开发模块 – Caffienate
- 前端模块化架构设计与实现(二|模块接口设计)
- 模块化编程的实践者 disconver 更新了用户模块
- ASP.NET Core模块化前后端分离快速开发框架介绍之4、模块化实现思路
- JavaScript模块化
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。