内容简介:简单概述: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模块化
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Think Python
Allen B. Downey / O'Reilly Media / 2012-8-23 / GBP 29.99
Think Python is an introduction to Python programming for students with no programming experience. It starts with the most basic concepts of programming, and is carefully designed to define all terms ......一起来看看 《Think Python》 这本书的介绍吧!