這篇六個點:1 Redux簡介 2 中間件 3 異步流 4 Redux與路由 5 Redux與組件 6 應用實例勋拟。
4.1 Redux簡介
4.1.1 Redux三大原則
1 單一數(shù)據(jù)源
在Redux思想里员寇,一個應用只有唯一的數(shù)據(jù)源嫁盲,好處是整個應用狀態(tài)都保存在一個對象中闹蒜,可以隨時取出整個應用的狀態(tài)進行持久化野哭,這樣的設計頁尾服務端渲染提供了可能双吆。
2 狀態(tài)是只讀的
在Redux中眨唬,并不會自己定義一個store,而是定義一個reducer好乐,reducer根據(jù)當前觸發(fā)的action對當前應用的狀態(tài)進行更新
3 狀態(tài)修改均由純函數(shù)完成
在redux里匾竿,通過定義reducer來確定狀態(tài)的修改,每一個reducer都是純函數(shù)蔚万,接受一定的輸入岭妖,必定得到一定的輸出。
4.1.2 Redux四個部分
整體結(jié)構(gòu)
import { createStore} from 'redux';
// reducer部分
const defaultState={
myValue:[],
inputValue:"默認值"
};
function reducer(state=defaultState,action) {
if (action.type==="add"){
const newState=JSON.parse(JSON.stringify(state));
newState.myValue.push(newState.inputValue);
console.log(newState);
return newState;
}
if (action.type==='change'){
const newState=JSON.parse(JSON.stringify(state));
newState.inputValue=action.value;
console.log(newState);
return newState;
}
return state;
}
// store 部分
const store=createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__&&window.__REDUX_DEVTOOLS_EXTENSION__()
);
class ReduxTest extends Component{
constructor(props){
super(props);
this.state=store.getState();
console.log(this.state);
this.handleClick=this.handleClick.bind(this);
this.handleChange=this.handleChange.bind(this);
// 狀態(tài)改變就執(zhí)行
store.subscribe(()=>{
this.setState(store.getState());
});
}
handleClick(){
const action={
type:'add'
};
store.dispatch(action);
}
handleChange(e){
const action={
type:'change',
value:e.target.value
};
store.dispatch(action);
}
render(){
return(
<div>
<input
value={this.state.inputValue||"HAHA"}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>改變</button>
</div>
)
}
}
- store
import { createStore} from 'redux';
const store=createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__&&window.__REDUX_DEVTOOLS_EXTENSION__()
);
Redux最核心的API,可以創(chuàng)建一個store對象昵慌,store又包含4個方法苔巨。
getState():獲取store中當前的狀態(tài)。
dispatch(action):分發(fā)一個action并返回這個action,這是唯一能改變store中數(shù)據(jù)的方式废离。
subscribe():注冊一個監(jiān)聽者侄泽,當store發(fā)生變化時會被調(diào)用。
- action
唯一可以改變狀態(tài)就是觸發(fā)action,它是一個普通對象蜻韭,用于描述已經(jīng)發(fā)生的事件悼尾。type是必須的,其他可以自定義。
handleChange(e){
const action={
type:'change',
value:e.target.value
};
store.dispatch(action);
}
//實際這么用
function addTo(e){
return{
type:'change',
value:e.target.value
}
}
handleChange(e){
store.dispatch(addTo(e));
}
- reducer
通過dispatch發(fā)起action之后肖方,最終是通過reducer指定如何state闺魏,reducer是用來修改狀態(tài)的,要注意的是不要在reducer中直接修改state俯画,推薦創(chuàng)建一個新的移入析桥,重新通過getState返回新的狀態(tài),最終通過subscribe重新渲染艰垂。 - connect
略
4.2 中間件
http://www.reibang.com/p/ae7b5a2f78ae
這是redux的數(shù)據(jù)流流程泡仗,當增加middleware后,就可以在途中對action進行截獲猜憎。
娩怎。且由于業(yè)務場景的多樣性,單純的修改 dispatch 和 reduce 人顯然不能滿足大家的需要胰柑,因此對 redux middleware 的設計是可以自由組合截亦,自由插拔的插件機制。也正是由于這個機制柬讨,我們在使用 middleware 時崩瓤,我們可以通過串聯(lián)不同的 middleware 來滿足日常的開發(fā),每一個 middleware 都可以處理一個相對獨立的業(yè)務需求且相互串聯(lián):
如上圖所示踩官,派發(fā)給 redux Store 的 action 對象却桶,會被 Store 上的多個中間件依次處理,如果把 action 和當前的 state 交給 reducer 處理的過程看做默認存在的中間件卖鲤,那么其實所有的對 action 的處理都可以有中間件組成的肾扰。值得注意的是這些中間件會按照指定的順序一次處理傳入的 action畴嘶,只有排在前面的中間件完成任務之后蛋逾,后面的中間件才有機會繼續(xù)處理 action,同樣的窗悯,每個中間件都有自己的“熔斷”處理,當它認為這個 action 不需要后面的中間件進行處理時区匣,后面的中間件也就不能再對這個 action 進行處理了。
而不同的中間件之所以可以組合使用,是因為 Redux 要求所有的中間件必須提供統(tǒng)一的接口亏钩,每個中間件的尉氏縣邏輯雖然不一樣莲绰,但只要遵循統(tǒng)一的接口就能和redux以及其他的中間件對話了。
4.3 Redux異步流
- 1 redux-thunk中間件
redux-thunk是用于在redux中處理異步action的中間件姑丑,它 統(tǒng)一了異步和同步action的調(diào)用方式蛤签,使得異步過程放在action級別解決,避免異步操作對component的耦合栅哀。
使用這個插件震肮,可以讓action創(chuàng)建函數(shù)先不返回一個action對象,而是返回一個函數(shù)留拾,函數(shù)傳遞兩個參數(shù)(dispatch,getState)
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import { createStore,applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
// reducer部分
const defaultState={
myValue:[],
inputValue:"默認值",
num:3
};
function reducer(state=defaultState,action) {
if (action.type=='addValue'){
const newState=JSON.parse(JSON.stringify(state));
newState.num++;
console.log(newState);
return newState;
}
return state;
}
// store 部分
const store=createStore(
reducer,
applyMiddleware(thunk)
);
class Thunk extends Component{
constructor(props){
super(props);
this.state=store.getState();
// console.log(this.state);
this.handleClick=this.handleClick.bind(this);
this.handleChange=this.handleChange.bind(this);
// 狀態(tài)改變就執(zhí)行
store.subscribe(()=>{
this.setState(store.getState());
});
}
addIfOdd(){
return (dispatch,getState)=>{
console.log('111');
if (this.state.num%2===0){
return false
}else {
setTimeout(()=>{
dispatch({type:'addValue'})
},2000)
}
}
}
handleClick(){
store.dispatch(this.addIfOdd());
console.log("222")
}
handleChange(){}
render(){
return(
<div>
<input
value={this.state.num||"HAHA"}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>改變</button>
</div>
)
}
}
- Redux-saga 略
4.4 Redux與路由
http://www.ruanyifeng.com/blog/2016/05/react_router.html
import {BrowserRouter,Route} from 'react-router-dom';
//要渲染組件內(nèi)的render
render() {
return (
<div>
<input
value={this.state.num || "HAHA"}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>改變</button>
<BrowserRouter>
<Route path='/' exact render={() => <div>首頁</div>}/>
<Route path='/detail' exact render={() => <div>詳情頁</div>}/>
</BrowserRouter>
</div>
)
}
ReactDOM.render(
<Thunk/>,
document.getElementById('root'),
function () {
console.log("加載完畢");
}
);
4.5 Redux與組件
4.5.1 容器型組件
4.5.2 展示型組件
4.5.3 Redux中的組件
4.6 應用實例
mkdir my-blog
cd my-blog
G:\react\my-blog> npm install --save react react-dom redux react-router react-redux react-router-redux whatwg-fetch
http://www.reibang.com/p/6a45e2dfc9d9/
app.js:整個應用的入口
views文件夾:頁面的入口文件戳晌,常在路由中引用
components文件夾:下面對應頁面的文件夾,里面一堆組件
containers :配置文件
layouts:公用的組件和樣式痴柔。如菜單沦偎,header等
redux:Redux store相關(guān)的配置
routes:路由相關(guān)的配置
-views/Home.js
import React,{Component} from 'react';
class Home extends Component {
constructor(props){
super(props);
};
render(){
return(
<h1>Home</h1>
)
}
}
export default Home;
- views/Detail.js
import React,{Component} from 'react';
class Detail extends Component {
constructor(props){
super(props);
};
render(){
return(
<h1>Detail</h1>
)
}
}
export default Detail;
- route/index.js
import React from 'react';
import {BrowserRouter,Route} from 'react-router-dom';
import Home from '../views/Home';
import Detail from '../views/Detail';
const routes=(
<BrowserRouter>
<Route path='/' component={Home}/>
<Route path='/detail' component={Detail}/>
</BrowserRouter>
);
export default routes;
- app.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import {BrowserRouter,Route} from 'react-router-dom';
import routes from './routes';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a className="App-link"
target="_blank" rel="noopener noreferrer"
>
Learn React
</a>
</header>
{routes}
</div>
);
}
export default App;
- index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
serviceWorker.unregister();
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
添加公用的組件
lauouts/Nav.js
import React,{Component} from 'react';
import {BrowserRouter as Router, Link} from 'react-router-dom';
class Nav extends Component {
constructor(props){
super(props);
};
render(){
return(
<nav>
<Link to='/'>首頁</Link>
</nav>
)
}
}
export default Nav;
lauouts/Frame.js
import React,{Component} from 'react';
import {Link} from 'react-router-dom';
import Nav from './Nav';
class Frame extends Component {
constructor(props){
super(props);
};
render(){
return(
<div>
<Nav/>
</div>
)
}
}
export default Frame;
修改routes/index.js
import React from 'react';
import {BrowserRouter,Route} from 'react-router-dom';
import Home from '../views/Home';
import Detail from '../views/Detail';
import Frame from '../layouts/Frame'
const routes=(
<BrowserRouter>
<Frame/>
<Route path='/' component={Home}/>
<Route path='/detail' component={Detail}/>
</BrowserRouter>
);
export default routes;
繼續(xù)修改App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import {BrowserRouter,Route} from 'react-router-dom';
import routes from './routes';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a className="App-link"
target="_blank" rel="noopener noreferrer"
>
Learn React
</a>
</header>
{routes}
</div>
);
}
export default App;
成功實現(xiàn)路由
準備首頁的數(shù)據(jù)
P242 后面看不懂 略