項目中應用到了redux,根據前人的代碼和網上百度到的資料總結如下,以免遺忘。
簡介
redux是一個狀態管理工具,隨着前端功能的增加,業務的複雜,將數據提取出組件是更好的方式,rudex是其中一種解決方案。
redux基本概念
redux由store、action、reducer 三部分組成。
store
store:倉庫,是一個包括項目所有的狀態數據的對象,redux提供了createStore函數來創建一個store
const store = createStore(reducer); //參數reducer下面介紹
action
action:動作,是一種包括類型和數據的對象,是reducer函數的參數,爲reducer提供參數來進行不同的處理,典型形式是:
{
type:"ADD_CART_NUM",
data: data
}
reducer
reducer:處理函數(我的翻譯),是一個具體的業務處理函數,位於action和disptch之間,被dispatch調用,根據傳入action對象數據進行處理並返回一個新的state。典型形式是:
// 通過判斷Action的類型,返回新的數據改變後的state對象,即使沒有任何狀態的改變,也要返回一個對象
// 注意:返回的state就是reducer提供的,也就是組件同步到的state
function counter(state = initState, action) { //state是reducer提供的默認值
const count = state.count
switch (action.type) {
case ADD_CART_NUM:
return { count: count + action.data.plusCount} //action.data是組件傳入的的值
default:
return state
}
}
dispatch
dispath: 分發函數(我的翻譯),是store內部的函數,傳入action並調用reducer,一般形式爲:
<button onClick={()=>store.dispatch(myAction.increaseCartNum())}>Increase</button>
//其中increaseCartNum返回一個action對象,在dispath內部進行對應reducer的調用
subscribe
subscribe: 註冊監聽函數(我的翻譯),也是store內部的函數,用於組件同步store中數據,一般形式爲:
componentWillMount(){
// 訂閱狀態變化
store.subscribe((state)=>this.setState(state)) //註冊到監聽器函數到store中
}
問題 store中是如何區分不同組件的state呢?
redux流轉流程
假設store中數據位於上層,組件位於下層,那麼有數據上行和數據下行兩種流轉。
數據下行
store通過props傳遞到具體組件,組件通過this.props.xxx拿到數據。
數據上行
組件中點擊按鈕等操作調用store的dispatch函數(對應的action作爲函數參數),dispatch函數內部調用相應的recuder函數,改變並返回store數據
react-reduce
在react應用中,利用react-redux來對兩者進行橋接使用。
react提供了一個函數和一個組件,簡化redux流程
connect函數:實現同步store數據到組件的props中、暴露action函數隱藏對dispatch調用reducer。一般形式爲
// 綁定store中的狀態到組件的props
const mapStateToProps = state => {
return {
state
}
}
//暴露action到組件props
const mapDispatchToProps = dispath => {
return bindActionCreators({
changeCartNum
}, dispath)
}
export default connect(mapStateToProps, mapDispatchToProps)(MiniCart) //其中MiniCart是組件
Provider標籤用於將store傳遞到每個組件中,一般形式爲:
ReactDOM.render(
<Provider store={store} >
<Router>
<LocaleProvider locale={zh_CN}><App /></LocaleProvider>
</Router>
</Provider >
, document.getElementById('root'));
整體代碼
//action.js aciton文件
export function changeCartNum(data) {
return {
type: 'CART_NUM',
data
}
}
//frontReducer.js 子reducer文件,不同模塊分爲不同reducer文件
const init = { //reducer的初始狀態
}
const Front = (state = init, action = {}) => {
switch (action.type) {
case 'CART_NUM':
//這裏可以添加一些處理函數,
return { ...state, ...action.data }
}
}
export default Front
//reducers.js 總reducers文件
import { combineReducers } from 'redux'
import Front from './FrontReducer';
const reducer = combineReducers({ //合併reducer文件
Front,
xxx,//不同的reducer文件
xxx,//不同的reducer文件
})
export default reducer
//index.js文件
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store} > //傳遞store到內部組件中
<Router>
<LocaleProvider locale={zh_CN}><App /></LocaleProvider>
</Router>
</Provider >
, document.getElementById('root'));
//B組件獲取cartNumber
import { changeCartNum } from '@actions';
class PublicTop extends React.Component {
render(){
....
<span >
購物車數量
{
this.props.state.Front.cartNum
}
</span>
.....
}
}
const mapStateToProps = state => {
return {
state
}
}
const mapDispatchToProps = dispath => {
return bindActionCreators({
changeCartNum
}, dispath)
}
export default connect(mapStateToProps, mapDispatchToProps)(MiniCart)
//A組件設置cartNumber
import { changeCartNum } from '@actions'; // 購物車數量
class Cart extends Component {
render(){
.......
//增加購物車數量按鈕的點擊處理函數
this.props.changeCartNum({ cartNum: totals })//totals是設置的值
.......
}
}
const mapStateToProps = state => {
return {
state
}
}
const mapDispatchToProps = dispath => {
return bindActionCreators({
changeCartNum
}, dispath)
}
export default connect(mapStateToProps, mapDispatchToProps)(Cart)
redux原生操作
以下程序是沒有使用react-redux的原生操作,包括store的簡易實現,用於理解redux的實現。
//reducer文件 counterReducer.js
// 提供一個初始的狀態
initState={
count: 0
}
// 通過判斷Action的類型,返回新的數據改變後的state對象,即使沒有任何狀態的改變,也要返回一個對象
export default function counter(state = initState, action) {
const count = state.count
switch (action.type) {
case INCREMENT:
return { count: count + 1 }
default:
return state
}
}
//主文件 index.js
// 創建一個store全局管理state和操作
const store = createStore(reducer);
// Provider在根組件<App>外面包了一層,App的所有子組件就默認都可以拿到store,通過組件的props傳遞
export default class Root extends Component {
render() {
return (
<Provider store={store}>
<App/>
</Provider>
)
}
}
// createStore的簡單實現
function createStore = ( reducer ) => {
let currentState; // 內部的狀態
let listeners = []; //所有的監聽者
const getState = () => currentState; // 獲取store中的state
// dispatch的操作就是內部執行reducer()函數,action和reducer在這兒產生交集,並且通知所有的監聽者
const dispatch = ( action ) => {
currentState = reducer(state, action); // 更新state
listeners.forEach(listener => listener());
}
// 訂閱事件
const subscribe = ( listener ) => {
listeners.push(listener);
return ()=>{
listeners = listeners.filter(l => l !== listener)
}
}
return {
getState,
dispatch,
subscribe
}
}
//組件文件
class Counter extends Component{
componentWillMount(){
// 訂閱狀態變化
store.subscribe((state)=>this.setState(state)) //註冊到監聽器函數到store中
}
render() {
return (
<div>
<span>{value}</span>
//點擊後dispatch事件類型
<button onClick={()=>store.dispatch(increaseAction.increase())}>Increase</button>
</div>
)
}
}