原Redux教程目錄: javascript
上一篇文章更新了目錄的1-10, 這篇教程更新11-17,看不懂的小夥伴必定要先去看第一篇甩上一篇教程地址 【譯】Redux入門教程(一)css
在繼續學習以前,來看看Redux的主要概念:html
Redux的reducer是由什麼組成的?vue
一個reducer是一個Javascript函數,它接收兩個參數: state和action, 一個reducer函數可使用一個switch語句(可是我更傾向於使用if語句)來處理每一個ation類型java
reducer經過action的type來計算下一個state, 並且,當沒有匹配到action的type的時候,它至少應該返回初始的statereact
當action的type匹配到一個有效的語句的時候,reducer就會計算下一個state而且返回一個新的對象npm
在以前的章節中,咱們建立了一個reducer,它除了返回初始的state什麼也沒作redux
打開src/js/reducers/index.js
,而且更新以下:bootstrap
// 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;
複製代碼
你發現了什麼?api
儘管上面是有效的代碼,可是他違背了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中避免變化有兩個要點:
Redux技巧: 隨着你的app愈來愈大,reducer也會愈來愈大,你能夠將一個大的reducer函數分割成多個函數,而且使用combineReducers將他們結合起來
接下來,咱們將會在控制檯玩Redux, 跟緊個人腳步!
我怕保證,下面的內容很快就能夠學會
我想要你經過瀏覽器的控制檯快速理解Redux是怎麼工做的
Redux自己是一個小型庫(2kKB), Redux的store暴露了簡單的API來管理state, store最重要的方法是:
咱們將會在瀏覽器的控制檯學習這些方法
爲次,咱們必須將咱們以前建立的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方法,在控制檯試驗它們
這就是開始學習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是怎麼協同工做的,咱們來建一個超級簡單的應用,這個應用由下面的組件組成:
react-redux是一個將Redux綁定到React的輕巧庫
接下來你將要認識一個很重要的方法connect
react-redux的connect到底作了什麼?豪不驚訝,他將React的組件和Redux的store聯繫了起來
使用connect, 你能夠傳遞兩個或者三個參數,具體取決於用途,下面是須要知道的基本的東西
react-redux中mapStateToProps是幹什麼用的?mapStateToProps 的做用正如它的名字:它鏈接Redux state的一部分到React組件的props,這樣,一個鏈接的React組件就可獲取到它所須要的那部分store的數據
mapDispatchToProps又是幹什麼用的呢?它作了和mapStateToProps 類似的事,可是是針對actions的, ,這樣,一個鏈接的React組件就能夠派發actions了
如今一切是否是都清楚了?若是不清楚,停下來,從新讀這份教程,我知道,有不少東西要學,要花不少時間,即便你不能立刻學會Redux也不要焦慮, 一切早晚都會清晰明瞭。
從下面開始,咱們要作點東西了!
咱們已經知道,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將會和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組件比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的中間件,堅持住!
本次更新完畢,有錯誤和翻譯不許確的地方,歡迎你們指出,一塊兒學習進步!!!
近期文章: