内容简介:上一篇文章更新了目录的1-10, 这篇教程更新11-17,看不懂的小伙伴一定要先去看第一篇在继续学习之前,来看看Redux的主要概念:Redux的reducer是由什么组成的?
上一篇文章更新了目录的1-10, 这篇教程更新11-17,看不懂的小伙伴一定要先去看第一篇
在继续学习之前,来看看Redux的主要概念:
- Redux的store是大脑:它掌管Redux所有部分
- 应用的状态通过store存在一个不可变的对象中
- 一旦store收到了一个action, 它会触发一个reducer函数
- 这个reducer返回下一个state
Redux的reducer是由什么组成的?
一个reducer是一个Javascript函数,它接收两个参数: state和action, 一个reducer函数可以使用一个switch语句(但是我更倾向于使用if语句)来处理每个ation类型
reducer通过action的type来计算下一个state, 而且, 当没有匹配到action的type的时候,它至少应该返回初始的state
当action的type匹配到一个有效的语句的时候,reducer就会计算下一个state并且返回一个新的对象
在之前的章节中,我们创建了一个reducer,它除了返回初始的state什么也没做
打开 src/js/reducers/index.js
,并且更新如下:
// src/js/reducers/index.js import { ADD_ARTICLE } from "../constants/action-types"; const initialState = { articles: [] }; function rootReducer(state = initialState, action) { if (action.type === ADD_ARTICLE) { state.articles.push(action.payload); } return state; } export default rootReducer; 复制代码
你发现了什么?
尽管上面是有效的代码,但是他违背了Redux的原则: 不变性(immutability)
数组的push方法不是一个纯函数:它改变了原数组,还有更严重的问题! 你记得Redux的第三个原则吗?state是不可变的并且任何时候都不能改变, 在我们的reducer里面我们改变了初始的对象!
我们需要修复。首先我们返回一个新的state,使用Object.assign得到的一个新的JavaScript对象, 通过这种方法,我们保证了原始的state的不变性, 接下来我们使用原生数组的conat方法代替push方法来保证原始数组不变,最终改造如下:
import { ADD_ARTICLE } from "../constants/action-types"; const initialState = { articles: [] }; function rootReducer(state = initialState, action) { if (action.type === ADD_ARTICLE) { return Object.assign({}, state, { articles: state.articles.concat(action.payload) }); } return state; } export default rootReducer; 复制代码
上面的例子保证了state是没有被修改的
初始的articels没有改变
初始的state对象也没有改变, 返回的state是初始state的一个拷贝
在Redux中避免变化有两个要点:
- 对数组使用concat, slice和...展开运算
- 对对象使用Objecy.assign和...展开运算
Redux技巧: 随着你的app越来越大,reducer也会越来越大,你可以将一个大的reducer函数分割成多个函数,并且使用combineReducers将他们结合起来
接下来,我们将会在控制台玩Redux, 跟紧我的脚步!
Redux store的方法
我怕保证,下面的内容很快就可以学会
我想要你通过浏览器的控制台快速理解Redux是怎么工作的
Redux本身是一个小型库(2kKB), Redux的store暴露了简单的API来管理state, store最重要的方法是:
- getState 获取应用的当前状态
- dispatch 派发一个action
- subscribe 监听state的变化
我们将会在浏览器的控制台学习这些方法
为次,我们必须将我们之前创建的store和action暴露成全局变量
创建一个src/js/index.js文件,更新如下:
import store from "../js/store/index"; import { addArticle } from "../js/actions/index"; window.store = store; window.addArticle = addArticle; 复制代码
现在打开src/index.js, 用下面的内容代替原先的内容:
import index from "./js/index" 复制代码
现在启动项目:
npm run start 复制代码
前往浏览器,打开http://localhost:8080/,并且按下F12打开控制台
因为我们已经将store暴露为全局变量了,我们可以获取到它的方法,试试吧!
开始获取当前的state:
store.getState() 复制代码
输出:
{articles: Array(0)} 复制代码
articles是空数组, 事实上,我们还没更新初始state
subscribe方法接受一个回调函数,当action被派发的时候这个回调函数会被触发,派发一个action就是通知store我们想要改变state
用下面的代码来注册回调函数:
store.subscribe(() => console.log('Look ma, Redux!!')) 复制代码
在Redux中要改变state,我们需要派发action, 派发action需要调用 dispath 方法
我们有一个action: addArticle-往state里面新增一项
用下面的代码派发action
store.dispatch(addArticle({ title: 'React Redux Tutorial for Beginners', id: 1 }) ) 复制代码
当你运行上面的代码后,你就会看到
Look ma, Redux!! 复制代码
为了验证state真的发生了变化,再次运行 store.getState()
输出 {articles: Array(1)}
这就是Redux最简单的形式了
难吗?
一步步来探索这三个Redux方法,在控制台试验它们
- getState 获取应用的当前状态
- dispatch 派发一个action
- subscribe 监听state的变化
这就是开始学习Redux你需要知道的所有东西
一旦你觉得有信心继续学习下面的内容了,我们将开始连接React和Redux!
连接React和Redux
学完Redux,我就意识到它并不复杂,我知道通过getState获取当前的state, 我知道怎么用dispatch派发一个action, 怎么用subscribe监听state的改变
然后我还是不知道 怎样将React和Redux结合在一起
我问我自己:我应该在一个React的组件中调用getState吗?我怎么在一个React组件中派发一个action?等等这些问题都困扰着我
Redux本身是一个独立的框架,你可以在普通的Javascript,或者框架如Angular,React中使用它,有种东西可以结合Redux和你最喜欢的框架。
对于React来说就是 react-redux
继续学习之前,先安装react-redux npm i react-redux --save-dev
为了演示React和Redux是怎么协同工作的,我们来建一个超级简单的应用,这个应用由下面的组件组成:
- 一个App组件
- 一个展示所有文章的列表组件
- 一个新增文章的表单组件
react-redux是什么
react-redux是一个将Redux绑定到React的轻巧库
接下来你将要认识一个很重要的方法 connect
react-redux的connect到底做了什么?豪不惊讶,他将React的组件和Redux的store联系了起来
使用connect, 你可以传递两个或者三个参数,具体取决于用途,下面是需要知道的基本的东西
- mapStateToProps函数
- mapDispatchToProps函数
react-redux中mapStateToProps是干什么用的?mapStateToProps 的作用正如它的名字: 它连接Redux state的一部分到React组件的props ,这样,一个连接的React组件就可获取到它所需要的那部分store的数据
mapDispatchToProps又是干什么用的呢?它做了和mapStateToProps 相似的事,但是是针对actions的, ,这样,一个连接的React组件就可以派发actions了
现在一切是不是都清楚了?如果不清楚,停下来,重新读这份教程,我知道,有很多东西要学,要花很多时间,即使你不能马上学会Redux也不要焦虑, 一切迟早都会清晰明了。
从下面开始,我们要做点东西了!
App component 和Redux store
我们已经知道,mapStateToProps连接Redux state的一部分到React组件的props中,你可能想知道,它可以连接整个Redux和React吗?是的,它不能 要想将Redux和React连接起来,我们需要使用 Provider
Provider是一个来自react-redux的高阶组件
用外行人的话来说,Provider包裹你的React应用, 并且让它可以感知到整个Redux的store
为什么要这样?我们知道Redux的store管理一切,React一定要可以和store沟通才可以获取state和派发actions
打开src/js/index.js, 清除整个文件内容,替换成下面的内容(如果你是通react-create-app来搭建环境的话,你需要修改的是src/index.js这个文件)
import React from "react"; import { render } from "react-dom"; import { Provider } from "react-redux"; import store from "./store/index"; import App from "./components/App.jsx"; // if you're in create-react-app import the files as: // import store from "./js/store/index"; // import App from "./js/components/App.jsx"; render( <Provider store={store}> <App /> </Provider>, // The target element might be either root or app, // depending on your development environment // document.getElementById("app") document.getElementById("root") ); 复制代码
看到了吗?Provider 包裹了整个React应用,而且它获取store作为prop传递
现在我们创建一个App组件,因为我们马上就需要它了,这个组件没什么特别:引入 List组件并且渲染自己
创建一个放组件的目录:
mkdir -p src/js/components 复制代码
在这目录里面增加一个App.jsx文件:
// src/js/components/App.jsx import React from "react"; import List from "./List"; const App = () => ( <div className="row mt-5"> <div className="col-md-4 offset-md-1"> <h2>Articles</h2> <List /> </div> </div> ); export default App; 复制代码
保存文件,接下来创建List组件
List component 和 Redux state
目前为止,我们做的都没什么特别的
但是我们的新组件List将会和Redux的store交互
简要回顾:连接React组件和Redux的关键是connect
connect接收至少一个参数
因为我们想要List组件获取文章列表,也就是要连接state.articles到这个组件中,怎么做呢?用 mapStateToProps
在 src/js/components创建一个List.jsx文件,内容如下
import React from "react"; import { connect } from "react-redux"; const mapStateToProps = state => { return { articles: state.articles }; }; const ConnectedList = ({ articles }) => ( <ul className="list-group list-group-flush"> {articles.map(el => ( <li className="list-group-item" key={el.id}> {el.title} </li> ))} </ul> ); const List = connect(mapStateToProps)(ConnectedList); export default List; 复制代码
List组件接收articels作为prop, articles是Redux的state的articels的一个拷贝, 它来自于reducer
const initialState = { articles: [] }; function rootReducer(state = initialState, action) { if (action.type === ADD_ARTICLE) { return Object.assign({}, state, { articles: state.articles.concat(action.payload) }); } return state; } 复制代码
时刻牢记: redux的state来自于reducers , 现在需要利用JSX的prop来生成articles列表
{articles.map(el => ( <li className="list-group-item" key={el.id}> {el.title} </li> ))}**** 复制代码
React技巧:养成用PropTypes 验证props的习惯,或者使用TypeScript会更好
最后,组件将List导出, List是无状态组件ConnectedList和Redux store结合的结果
是否依然困惑?我也是,理解connect如何运作需要一些时间,不要怕, 通往Redux的学习之路充满了'啊?-啊!'的时刻
我建议你休息一下,再探索研究connect和mapStateToProps
一旦你对这些都胸有成竹了,你可以继续进行下面的学习了!
Form component 和 Redux actions
我们即将创建的Form组件比List组件复杂一点,它是一个表单,可以添加新条目到应用中
除此之外,它是一个 有状态的组件
一个有状态的组件是一个有自己的state的React组件
我们现在在谈论用Redux管理状态,你为什么要给Form组件自身状态呢?
即使使用Redux,我们也可以有有状态的组件存在
并不是每个应用的状态都应该放在Redux中
在这个例子中,我不想要其他的组件知道这个Form组件本自身的state
form组件包含一些通过提交操作跟新本地状态的逻辑
它接收一个Redux的action,这样,它可以通过派发addArticle这个action更新全局的state
在src/js/components新增Form.jsx , 内容如下
// src/js/components/Form.jsx import React, { Component } from "react"; import { connect } from "react-redux"; import uuidv1 from "uuid"; import { addArticle } from "../actions/index"; function mapDispatchToProps(dispatch) { return { addArticle: article => dispatch(addArticle(article)) }; } class ConnectedForm extends Component { constructor() { super(); this.state = { title: "" }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event) { this.setState({ [event.target.id]: event.target.value }); } handleSubmit(event) { event.preventDefault(); const { title } = this.state; const id = uuidv1(); this.props.addArticle({ title, id }); this.setState({ title: "" }); } render() { const { title } = this.state; return ( <form onSubmit={this.handleSubmit}> <div className="form-group"> <label htmlFor="title">Title</label> <input type="text" className="form-control" id="title" value={title} onChange={this.handleChange} /> </div> <button type="submit" className="btn btn-success btn-lg"> SAVE </button> </form> ); } } const Form = connect(null, mapDispatchToProps)(ConnectedForm); export default Form; 复制代码
除了mapDispatchToProps和connect,这个组件是标准的React组件
mapDispatchToProps连接Redux的actions到React组件的props,这样,一个连接的组件就可以派发actions了
通过handleSubmit方法你可以明白action是如何被派发的
// ... handleSubmit(event) { event.preventDefault(); const { title } = this.state; const id = uuidv1(); this.props.addArticle({ title, id }); // Relevant Redux part!! // ... } // ... 复制代码
最后组件导出为Form,它是ConnectedForm和Redux store结合后的结果
注意:当mapStateToProps 不存在的时候,connect的第一个参数必须是null,否则你会得到TypeError:dispatch 不是一个函数
我们的组件都设置好了!
更新App组件,引入Form 组件
import React from "react"; import List from "./List.jsx"; import Form from "./Form.jsx"; const App = () => ( <div className="row mt-5"> <div className="col-md-4 offset-md-1"> <h2>Articles</h2> <List /> </div> <div className="col-md-4 offset-md-1"> <h2>Add a new article</h2> <Form /> </div> </div> ); export default App; 复制代码
安装uuid
npm i uuid --save-dev 复制代码
运行应用
npm run start 复制代码
前往浏览器打开http://localhost:8080,你会看到如下的页面
页面没有任何奇特的东西,但是它显示了React和Redux正在工作!
左边的List组件连接着Redux的store,当你添加一个条目的时候,它会重新渲染
如果你在页面上什么都没看到,确保你在 src/js/index.js中写了document.getElementById(“app”) ****
来匹配一个页面中真实存在的元素
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" > <title>How to set up React, Webpack, and Babel</title> </head> <body> <div class="container"> <div id="root"> </div> </div> </body> </html> 复制代码
别忘记引入Bootstrap ,但是我们还没做完,接下来,我们来看看Redux的中间件,坚持住!
本次更新完毕,有错误和翻译不准确的地方,欢迎大家指出,一起学习进步!!!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
机器学习实践指南
麦好 / 机械工业出版社 / 2014-4-1 / 69.00
《机器学习实践指南:案例应用解析》是机器学习及数据分析领域不可多得的一本著作,也是为数不多的既有大量实践应用案例又包含算法理论剖析的著作,作者针对机器学习算法既抽象复杂又涉及多门数学学科的特点,力求理论联系实际,始终以算法应用为主线,由浅入深以全新的角度诠释机器学习。 全书分为准备篇、基础篇、统计分析实战篇和机器学习实战篇。准备篇介绍了机器学习的发展及应用前景以及常用科学计算平台,主要包括统......一起来看看 《机器学习实践指南》 这本书的介绍吧!