添加 redux

写过 react 稍微复杂一些应用的话,应该都对 redux(mobx)有一定的了解.这次将 redux 引入到项目中

因为之前写项目的习惯,更喜欢使用 redux-thunk 改写 dispatch 进行异步请求.

redux-thunk 改写了 dispatch API,使其具备接受一个函数作为参数的能力.既然是函数,就可以执行,也就可以发起 ajax 请求了.

yarn add next-redux-wrapper react-redux redux redux-devtools-extension es6-promise redux-thunk redux-logger

创建 actions

首先,还是将 action 的常量抽离出去单独做一个文件 actionTypes.js.当前项目是刚起步,所以暂时不需要将 reducer 和 action 分成小模块.

新建./redux/actions.js

import { actionTypes } from "./actionTypes";

export function failure(error) {
return {
type: actionTypes.FAILURE,
error
};
} export function increment() {
return { type: actionTypes.INCREMENT };
} export function decrement() {
return { type: actionTypes.DECREMENT };
} export function reset() {
return { type: actionTypes.RESET };
} export function loadData() {
return { type: actionTypes.LOAD_DATA };
} export function loadDataSuccess(data) {
return {
type: actionTypes.LOAD_DATA_SUCCESS,
data
};
} export function startClock() {
return { type: actionTypes.START_CLOCK };
} export function tickClock(isServer) {
return {
type: actionTypes.TICK_CLOCK,
light: !isServer,
ts: Date.now()
};
}

创建 action 常量文件

新建./redux/actionTypes.js

export const actionTypes = {
FAILURE: "FAILURE",
INCREMENT: "INCREMENT",
DECREMENT: "DECREMENT",
RESET: "RESET",
LOAD_DATA: "LOAD_DATA",
LOAD_DATA_SUCCESS: "LOAD_DATA_SUCCESS",
START_CLOCK: "START_CLOCK",
TICK_CLOCK: "TICK_CLOCK"
};

创建 reducer

新建./redux/reducer.js

import { actionTypes } from "./actionTypes";

export const exampleInitialState = {
count: 0,
error: false,
lastUpdate: 0,
light: false,
placeholderData: null
}; function reducer(state = exampleInitialState, action) {
switch (action.type) {
case actionTypes.FAILURE:
return {
...state,
...{ error: action.error }
}; case actionTypes.INCREMENT:
return {
...state,
...{ count: state.count + 1 }
}; case actionTypes.DECREMENT:
return {
...state,
...{ count: state.count - 1 }
}; case actionTypes.RESET:
return {
...state,
...{ count: exampleInitialState.count }
}; case actionTypes.LOAD_DATA_SUCCESS:
return {
...state,
...{ placeholderData: action.data }
}; case actionTypes.TICK_CLOCK:
return {
...state,
...{ lastUpdate: action.ts, light: !!action.light }
}; default:
return state;
}
} export default reducer;

新建./redux/store.js

import { applyMiddleware, createStore } from "redux";
import thunkMiddleware from "redux-thunk"; import rootReducer, { exampleInitialState } from "./reducer"; const bindMiddleware = middleware => {
if (process.env.NODE_ENV !== "production") {
const { composeWithDevTools } = require("redux-devtools-extension");
// 开发模式打印redux信息
const { logger } = require("redux-logger");
middleware.push(logger);
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
}; function configureStore(initialState = exampleInitialState) {
const store = createStore(rootReducer, initialState, bindMiddleware([thunkMiddleware]));
return store;
} export default configureStore;

修改 pages/_app.js 文件

redux 会创建一个全局的 state 包裹在 app 的最高一级,然后通过注入的形式添加到下级的各级组件中.现在就在自定义的_app.js 文件中添加 Provider

import React from "react";
import App from "next/app";
import "../assets/css/styles.less";
import { Provider } from "react-redux";
import withRedux from "next-redux-wrapper";
import createStore from "../redux/store"; class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
let pageProps = {}; if (Component.getInitialProps) {
pageProps = await Component.getInitialProps({ ctx });
} return { pageProps };
} render() {
const { Component, pageProps, store } = this.props;
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
);
}
} export default withRedux(createStore)(MyApp);

react-redux 中 mapDispatchToProps 的几种方式

讲一下关于 redux 的集中注入方式,同时将 redux 如何在下级组件使用的方式展示出来.

重点知道什么是 action,什么是 action 生成器,什么又是触发 action 函数

action 是一个对象

{
type: actionTypes.TICK_CLOCK,
light: !isServer,
ts: Date.now()
}

action 生成器是一个函数

function tickClock(isServer) {
return {
type: actionTypes.TICK_CLOCK,
light: !isServer,
ts: Date.now()
};
}

触发 action 函数

function dispatchTickClock(dispatch){
return dispatch(tickClock(false))
}

mapDispatchToProps

[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function)

传递对象

  • 如果传递的是一个对象,那么每个定义在该对象的函数都将被当作 Redux action creator,对象所定义的方法名将作为属性名;每个方法将返回一个新的函数,函数中 dispatch 方法会将 action creator 的返回值作为参数执行,Redux 自动发出
import React, { Component } from "react";
import { connect } from "react-redux";
import { increment, decrement, reset } from "../../redux/actions";
import { Button } from "antd"; class Counter extends Component {
render() {
const { count, increment, decrement, reset } = this.props;
return (
<div>
<style jsx>{`
div {
padding: 0 0 20px 0;
}
`}</style>
<h1>
Count: <span>{count}</span>
</h1>
<Button type="primary" onClick={increment}>
当前counter +1
</Button>
<Button type="primary" onClick={decrement}>
当前counter -1
</Button>
<Button type="primary" onClick={reset}>
当前counter Reset
</Button>
</div>
);
}
} function mapStateToProps(state) {
return {
count: state.count
};
} const mapActionCreators = {
increment,
decrement,
reset
}; export default connect(
mapStateToProps,
mapActionCreators
)(Counter);

bindActionCreators 辅助函数

  • 如果传递的是一个函数,该函数将接收一个 dispatch 函数,然后由你来决定如何返回一个对象,这个对象通过 dispatch 函数与 action creator 以某种方式绑定在一起(提示:你也许会用到 Redux 的辅助函数 bindActionCreators()。
import React, { Component } from "react";
import { connect } from "react-redux";
import { increment, decrement, reset } from "../../redux/actions";
import { Button } from "antd";
import { bindActionCreators } from "redux"; class Counter extends Component {
render() {
const { count, increment, decrement, reset } = this.props;
return (
<div>
<style jsx>{`
div {
padding: 0 0 20px 0;
}
`}</style>
<h1>
Count: <span>{count}</span>
</h1>
<Button type="primary" onClick={increment}>
当前counter +1
</Button>
<Button type="primary" onClick={decrement}>
当前counter -1
</Button>
<Button type="primary" onClick={reset}>
当前counter Reset
</Button>
</div>
);
}
} function mapStateToProps(state) {
return {
count: state.count
};
} function mapDispatchToProps(dispatch) {
return {
increment: bindActionCreators(increment, dispatch),
decrement: bindActionCreators(decrement, dispatch),
reset: bindActionCreators(reset, dispatch)
};
} export default connect(
mapStateToProps,
mapDispatchToProps
)(Counter);

上面的写法 mapDispatchToProps 可以将函数以不同的名称加入到 props 中,如果不需要变更名称,也可以简写

function mapDispatchToProps(dispatch) {
return bindActionCreators({increment,decrement,reset},dispatch);
}

其中 action 生成器

export function increment() {
return { type: actionTypes.INCREMENT };
} export function decrement() {
return { type: actionTypes.DECREMENT };
}

这里直接使用了 redux 的 bindActionCreators 辅助函数去绑定 action 触发函数

参数传入函数且不用 bindActionCreators 辅助函数

import React, { Component } from "react";
import { connect } from "react-redux";
import { increment, decrement, reset } from "../../redux/actions";
import { Button } from "antd"; class Counter extends Component {
render() {
const { count, increment, decrement, reset } = this.props;
return (
<div>
<style jsx>{`
div {
padding: 0 0 20px 0;
}
`}</style>
<h1>
Count: <span>{count}</span>
</h1>
<Button type="primary" onClick={increment}>
当前counter +1
</Button>
<Button type="primary" onClick={decrement}>
当前counter -1
</Button>
<Button type="primary" onClick={reset}>
当前counter Reset
</Button>
</div>
);
}
} function mapStateToProps(state) {
return {
count: state.count
};
} function mapActionCreators(dispatch) {
return {
increment: () => {
return dispatch(increment());
},
decrement: () => {
return dispatch(decrement());
},
reset: () => {
return dispatch(reset());
}
};
} export default connect(
mapStateToProps,
mapActionCreators
)(Counter);

dispatch 注入组件

  • 如果你省略这个 mapDispatchToProps 参数,默认情况下,dispatch 会注入到你的组件 props 中。
import React, { Component } from "react";
import { connect } from "react-redux";
import { increment, decrement, reset } from "../../redux/actions";
import { Button } from "antd"; class Counter extends Component {
increment = () => {
this.props.dispatch(increment());
}; decrement = () => {
this.props.dispatch(decrement());
}; reset = () => {
this.props.dispatch(reset());
};
render() {
const { count } = this.props;
return (
<div>
<style jsx>{`
div {
padding: 0 0 20px 0;
}
`}</style>
<h1>
Count: <span>{count}</span>
</h1>
<Button type="primary" onClick={this.increment}>
当前counter +1
</Button>
<Button type="primary" onClick={this.decrement}>
当前counter -1
</Button>
<Button type="primary" onClick={this.reset}>
当前counter Reset
</Button>
</div>
);
}
} function mapStateToProps(state) {
return {
count: state.count
};
} export default connect(mapStateToProps)(Counter);

其中 action 生成器

export function increment() {
return { type: actionTypes.INCREMENT };
} export function decrement() {
return { type: actionTypes.DECREMENT };
}

这里的方法就是 increment()生成器返回一个 action,然后交由 action 触发器 dispatch 去触发

使用 redux-thunk

import React, { Component } from "react";
import { connect } from "react-redux";
import { increment, decrement, reset } from "../../redux/actions";
import { Button } from "antd"; class Counter extends Component {
render() {
const { count, increment, decrement, reset } = this.props;
return (
<div>
<style jsx>{`
div {
padding: 0 0 20px 0;
}
`}</style>
<h1>
Count: <span>{count}</span>
</h1>
<Button type="primary" onClick={increment}>
当前counter +1
</Button>
<Button type="primary" onClick={decrement}>
当前counter -1
</Button>
<Button type="primary" onClick={reset}>
当前counter Reset
</Button>
</div>
);
}
} function mapStateToProps(state) {
return {
count: state.count
};
} const mapActionCreators = {
increment,
decrement,
reset
}; export default connect(
mapStateToProps,
mapActionCreators
)(Counter);

其中 action 生成器需要修改为返回函数,由于返回的是一个函数,redux 可以在里面执行一些异步操作,action 也可以用来进行网络请求

export function increment() {
return dispatch => {
dispatch({ type: actionTypes.INCREMENT });
};
} export function decrement() {
return dispatch => {
dispatch({ type: actionTypes.DECREMENT });
};
} export function reset() {
return dispatch => {
dispatch({ type: actionTypes.RESET });
};
}

使用装饰器

yarn add @babel/plugin-proposal-decorators --dev
yarn add babel-plugin-transform-decorators-legacy --dev

然后修改.babelrc

{
"presets": ["next/babel"],
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
],
[
"import",
{
"libraryName": "antd",
"style": "less"
}
]
]
}

将组件的代码更新

import React, { Component } from "react";
import { connect } from "react-redux";
import { increment, decrement, reset } from "../../redux/actions";
import { Button } from "antd"; @connect(
state => ({ count: state.count }),
dispatch => bindActionCreators({ increment, decrement, reset }, dispatch)
)
class Counter extends Component {
render() {
const { count, increment, decrement, reset } = this.props;
return (
<div>
<style jsx>{`
div {
padding: 0 0 20px 0;
}
`}</style>
<h1>
Count: <span>{count}</span>
</h1>
<Button type="primary" onClick={increment}>
当前counter +1
</Button>
<Button type="primary" onClick={decrement}>
当前counter -1
</Button>
<Button type="primary" onClick={reset}>
当前counter Reset
</Button>
</div>
);
}
} export default Counter;

如果你之前没有使用装饰器的话,vscode 会报出一个警告,现在去除这个警告

tsconfig.json

{
"compilerOptions": {
"experimentalDecorators": true,
"allowJs": true,
},
}

没有 tsconfig.json 就用 jsconfig.json

{
"compilerOptions": {
"experimentalDecorators": true
}
}

[错误解决]https://github.com/zeit/next.js/issues/5231

[Next] 四.在next中引入redux的更多相关文章

  1. go微服务系列(四) - http api中引入protobuf

    1. protobuf相关依赖安装 2. 改造之前的client 2.1 新建proto文件 2.2 运行protoc命令生成go文件 2.3 然后把原来的map修改成具体的类型就可以了 3. 处理j ...

  2. 在React中使用Redux

    这是Webpack+React系列配置过程记录的第六篇.其他内容请参考: 第一篇:使用webpack.babel.react.antdesign配置单页面应用开发环境 第二篇:使用react-rout ...

  3. 在微信小程序中使用redux

    本文主要讲述,在微信小程序中如何使用redux DEMO 需要解决的问题 如何在小程序中引入redux状态管理库,及它相关的插件? 微信小程序没有清晰的异步api,便于thunkMiddleware处 ...

  4. HTML中引入CSS的四种常用方法及各自的缺点

    在HTML中引入CSS的方法主要有四种,它们分别是行内式.内嵌式.链接式和导入式. 1.行内式 行内式是在标记的style属性中设定CSS样式.这种方式没有体现出CSS的优势,不推荐使用.格式如下: ...

  5. react项目中引入了redux后js控制路由跳转方案

    如果你的项目中并没有用到redux,那本文你可以忽略 问题引入 纯粹的单页面react应用中,通过this.props.history.push('/list')就可以进行路由跳转,但是加上了redu ...

  6. 【React踩坑记四】React项目中引入并使用js-xlsx上传插件(结合antdesign的上传组件)

    最近有一个前端上传并解析excel/csv表格数据的需求. 于是在github上找到一个14K star的前端解析插件 github传送门 官方也有,奈何实在太过于浅薄.于是做了以下整理,避免道友们少 ...

  7. 优雅的在React项目中使用Redux

    概念 首先我们会用到哪些框架和工具呢? React UI框架 Redux 状态管理工具,与React没有任何关系,其他UI框架也可以使用Redux react-redux React插件,作用:方便在 ...

  8. 页面中引入js的几种方法

    通常大家最为熟悉的是一下两种方法: 在页面中直接写入<script type="text/javascript">js代码</script>. 在页面中引入 ...

  9. React-Native 开发(二) 在react-native 中 运用 redux

    前提: 一个小web前端,完全不会android 跟iOS 的开发,首次接触,有很多不懂的问题.请见谅. 环境: win7 上一篇 : React-Native 开发(一) Android环境部署,H ...

随机推荐

  1. Misc套路记录

    1.对于给定的二维码图片不能直接扫描出来的可以进行反色在进行扫描,反色可以直接选中图片然后就会进行反色.2.局域网中抓取的数据包的加密方式可能是aes加密.3.凯撒加密可能是变种的凯撒加密,可能奇数偶 ...

  2. spark streaming 5: InputDStream

    InputDStream的继承关系.他们都是使用InputDStream这个抽象类的接口进行操作的.特别注意ReceiverInputDStream这个类,大部分时候我们使用的是它作为扩展的基类,因为 ...

  3. 1、WebSphere Application Server的下载以及安装

    最近在做农行相关的项目,我们的后台需要发布到农行WebSphere Application Server上,因此学习一下: 一.WebSphere 是什么? WebSphere 为 SOA (面向服务 ...

  4. vs install 安装时自动添加注册表

    思路:使用自定义 解决方案添加类库项目 添加安装程序类 随后右键查看代码 在构造函数添加事件 同时完成这个事件,在此事件中根据需要添加我们需要的内容,此处为添加注册表,并根据安装目录添加url pro ...

  5. tensorflow读取图片案例

    1.知识点 """ 1.图片读取流程与API: 1.构造图片文件队列 文件队列API: a)tf.train.string_input_producer(string_t ...

  6. IIS asp.net 中出现未能加载文件或程序集“System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”或它的某一个依赖项。系统找不到指定的文件。

    分析器错误消息: 未能加载文件或程序集“System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”或 ...

  7. windows下安装RabbitMQ【我】

    windows下 安装 rabbitMQ rabbitMQ是一个在AMQP协议标准基础上完整的,可服用的企业消息系统.它遵循Mozilla Public License开源协议,采用 Erlang 实 ...

  8. java.lang.Double[] 转 double[]

    Double[] v = list.stream().map(item -> item.getRespTime()).toArray(Double[]::new); Mean mean = ne ...

  9. 使用jdk自带工具jvisualvm 分析内存dump文件

    1.获取dump文件 使用 以下命令 创建 进程PID = 16231的 dump文件,命名为 order.hprof jmap -dump:format=b,file=order.hprof 162 ...

  10. php下关于字符串与数组的相互转换的函数

    public static function string2array($tags)    {        return preg_split('/\s*,\s*/',trim($tags),-1, ...