上:
JavaScript純函數(shù):
- 確定的輸入,一定會產(chǎn)生確定的輸出咐鹤;
- 函數(shù)在執(zhí)行過程中拗秘,不能產(chǎn)生副作用;
React中要求我們無論是函數(shù)還是class聲明一個組件慷暂,這個組件必須是像純函數(shù)一樣聘殖,保護props不被修改,redux中,reducer要求是一個純函數(shù)行瑞。
(一):Redux定義:是JavaScript狀態(tài)容器奸腺,提供了可預(yù)測的狀態(tài)管理
核心理念——store
管理數(shù)據(jù)
const initialState = {
counter: 0
}
核心理念——action
所有數(shù)據(jù)的變化,必須通過派發(fā)(dispatch) action來更新血久;
它是一個普通的js對象突照,用來描述這次更新的type和content;
// actions
const action1 = { type: "INCREMENT" };
const action2 = { type: "DECREMENT" };
const action3 = { type: "ADD_NUMBER", num: 5 };
const action4 = { type: "SUB_NUMBER", num: 12 };
核心理念——reducer
它是一個純函數(shù)氧吐;負責將傳入的state和action結(jié)合起來生成一個新的state讹蘑;
// reducer
function reducer(state = initialState, action) {
switch (action.type) {
case "INCREMENT":
return { ...state, counter: state.counter + 1 }
case "DECREMENT":
return { ...state, counter: state.counter - 1 }
case "ADD_NUMBER":
return { ...state, counter: state.counter + action.num }
case "SUB_NUMBER":
return { ...state, counter: state.counter - action.num }
default:
return state;
}
}
結(jié)合使用:
// store(創(chuàng)建的時候需要傳入一個reducer)
const store = redux.createStore(reducer)
// 訂閱store的修改
store.subscribe(() => {
console.log("counter:", store.getState().counter);
})
// 派發(fā)action
store.dispatch(action1);
store.dispatch(action2);
store.dispatch(action2);
store.dispatch(action3);
store.dispatch(action4);
完整測試案例:
- yarn init
- yarn add redux
- 創(chuàng)建src目錄末盔,創(chuàng)建index.js
- 修改package.json執(zhí)行index.js
“scripts”:{
"start": "node src/index.js"
}
注意: 從node v13.2.0開始,node才對ES6模塊化提供了支持座慰。
node v13.2.0之前陨舱,需要進行如下操作:
在package.json中添加屬性: "type": "module";
在執(zhí)行命令中添加如下選項:node --experimental-modules src/index.js;
node v13.2.0之后版仔,只需要進行如下操作:
在package.json中添加屬性: "type": "module"
目錄結(jié)構(gòu):
store/index.js
import redux from 'redux';
import reducer from './reducer.js';
const store = redux.createStore(reducer);
export default store;
store/reducer.js
import {
ADD_NUMBER,
SUB_NUMBER,
INCREMENT,
DECREMENT
} from './constants.js';
const defaultState = {
counter: 0
}
function reducer(state = defaultState, action) {
switch (action.type) {
case ADD_NUMBER:
return { ...state, counter: state.counter + action.num };
case SUB_NUMBER:
return { ...state, counter: state.counter - action.num };
case INCREMENT:
return { ...state, counter: state.counter + 1 };
case DECREMENT:
return { ...state, counter: state.counter - 1 };
default:
return state;
}
}
export default reducer;
store/actionCreators.js
import {
ADD_NUMBER,
SUB_NUMBER,
INCREMENT,
DECREMENT
} from './constants.js';
// 寫法一:
// export function addAction(num) {
// return {
// type: "ADD_NUMBER",
// num
// }
// }
// 寫法二:
// export const addAction = (num) => {
// return {
// type: "ADD_NUMBER",
// num
// }
// }
// 寫法三:
export const addAction = num => ({
type: ADD_NUMBER,
num
});
export const subAction = num => ({
type: SUB_NUMBER,
num
});
export const incAction = () => ({
type: INCREMENT
});
export const decAction = () => ({
type: DECREMENT
});
store/constants.js
export const ADD_NUMBER = "ADD_NUMBER";
export const SUB_NUMBER = "SUB_NUMBER";
export const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";
完整react+redux案例:
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<App />
document.getElementById('root')
);
src/App.js
import React, { PureComponent } from 'react';
import Home from './pages/home';
import About from './pages/about';
export default class App extends PureComponent {
render() {
return (
<div>
<Home/>
<About/>
</div>
)
}
}
src/store/index.js
import { createStore } from 'redux';
iimport reducer from './reducer.js';
const store = createStore(reducer);
export default store;
其它三個文件store/actionCreators.js游盲、store/reducer.js、store/constants.js不變;
src/store/index.js
pages/about.js
核心:
componentDidMount 中定義數(shù)據(jù)的變化蛮粮,當數(shù)據(jù)發(fā)生變化時重新設(shè)置 counter;
在發(fā)生點擊事件時益缎,調(diào)用store的dispatch來派發(fā)對應(yīng)的action
import React, { PureComponent } from 'react';
import store from '../store';
import { subAction } from "../store/actionCreators";
export default class About extends PureComponent {
constructor(props) {
super(props);
this.state = {
counter: store.getState().counter
}
}
componentDidMount() {
this.unsubscribue = store.subscribe(() => {
this.setState({
counter: store.getState().counter
})
})
}
componentWillUnmount() {
this.unsubscribue();
}
render() {
return (
<div>
<hr/>
<h1>About</h1>
<h2>當前計數(shù): {this.state.counter}</h2>
<button onClick={e => this.decrement()}>-1</button>
<button onClick={e => this.subNumber(5)}>-5</button>
</div>
)
}
decrement() {
store.dispatch(subAction(1));
}
subNumber(num) {
store.dispatch(subAction(num));
}
}
pages/home.js
import React, { PureComponent } from "react";
import store from "../store";
import { addAction } from "../store/actionCreators";
export default class Home extends PureComponent {
constructor(props) {
super(props);
this.state = {
counter: store.getState().counter,
};
}
componentDidMount() {
this.unsubscribue = store.subscribe(() => {
this.setState({
counter: store.getState().counter,
});
});
}
componentWillUnmount() {
this.unsubscribue();
}
render() {
return (
<div>
<h1>Home</h1>
<h2>當前計數(shù): {this.state.counter}</h2>
<button onClick={(e) => this.increment()}>+1</button>
<button onClick={(e) => this.addNumber(5)}>+5</button>
</div>
);
}
increment() {
store.dispatch(addAction(1));
}
addNumber(num) {
store.dispatch(addAction(num));
}
}
代碼在下部分優(yōu)化