距離上篇文章已經過去了大半個月,本來打算只更新代碼,最後還是決定把做的過程中遇到的問題記錄出來,說不定就可以幫助一些同學,也算是幸事,如果沒有,那就當作自己梳理知識點吧!
該篇文章主要講述以下知識點:
- 如何mock數據
- React組件中修改Redux中的數據
mock數據
有三種方式:
- 利用node搭服務,mock數據
- 利用現成網上mock服務,比如easy mock
- 本地json數據模擬請求
介紹下本地數據模擬請求,利用axios請求數據:
import axios from 'axios';
const BaseUrl='./mock/homeData/';
const getHome=()=>{
return axios.get(BaseUrl+'home.json').then(res=>{
return res.data
})
}
const getArticleList=()=>{
return axios.get(BaseUrl+'articleList.json').then(res=>{
return res.data
})
}
export { getHome,getArticleList }
本地json數據存放位置如下
React組件中修改Redux中的數據
定義好state,reducer
//userReducer.js
// 1.定義默認數據
let initialState = {
userId:'',
userName:'',//實際項目與此不同
userImage: '',
userDesc:''
}
// action creators
export const actions = {
login: (userInfo) => {
return {type: 'CHANGE_USER',userName: userInfo.username,userId:userInfo.userId}
},
logout:()=>{
return {type:'LOGOUT'}
}
};
// 2.Reducer
const userReducer = (state = initialState, action) => {
switch (action.type) {
case 'CHANGE_USERIMAGE':
return { ...state, userImage: action.userImage }
case 'CHANGE_USERID':
return { ...state,userId:action.userId}
case 'CHANGE_USER':
return { ...state,userName:action.userName,userId:action.userId}
case 'LOGOUT':
return {...state,userName:'',userId:''}
default:
return state
}
}
// 3.導出
export default userReducer;
在組件中操作修改state
...
this.props.login({username:username,userId:userId});//修改存儲在redux中的用戶信息
...
const mapStateToProps = (state, ownProps) => {
return {
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return bindActionCreators({
login: authActions.login
},dispatch);
}
Login=connect(mapStateToProps,mapDispatchToProps)(Login)
export default Login;
Redux通過reducer解析action。reducer是一個普通的js函數,接受action作爲參數,然後返回一個新的state。
三大原則
- 唯一數據源
- 保持state狀態只讀
- state的改變必須通過純函數完成
Redux
Redux應用只維護一個全局的狀態對象,存在Redux的store中。程序任何時候都不能直接去修改狀態state,如果需要修改state,必須發送一個action,通過action去描述如何修改state。action描述了改變state的意圖,真正對state作出修改的是reducer。reducer必須是純函數,所以reducer在收到action後,不能直接去修改原來的狀態state,而是應該創建一個新的狀態對象返回。
純函數需要滿足兩個條件:
(1)對於同樣的參數值,函數返回結果總是相同的,就是函數結果不依賴任何在程序執行過程中可能改變的變量。
(2)函數的執行過程不會產生比的副作用,例如會修改外部對象,有時會去修改頁面標題,這就是副作用。
數據流向的一個簡單示意圖,來自Flux官網
可以看出Redux應用的主要組成有action,reducer和store。
action
action是Redux中的信息載體,是store的唯一來源。把action發給store必須通過store中的dispach方法。其實action就是一個普通的js對象,但是每個action必須有一個type屬性用來描述action類型,要做什麼操作。除了type屬性歪,action的結構完全由你決定,不過應該保證可以清晰描述使用場景。
function logout(info){
return {
type:'LOGOUT',
info
}
}
reducer
reducer根據action做出響應,決定如何修改應用狀態state。其實在寫reducer之前就應該設計好state。
state:
// 1.定義默認數據
let initialState = {
userId:'',
userName:'',//實際項目與此不同
userImage: '',
userDesc:''
}
reducer:
// 2.Reducer
const userReducer = (state = initialState, action) => {
switch (action.type) {
case 'CHANGE_USERIMAGE':
return { ...state, userImage: action.userImage }
case 'CHANGE_USERID':
return { ...state,userId:action.userId}
case 'CHANGE_USER':
return { ...state,userName:action.userName,userId:action.userId}
case 'LOGOUT':
return {...state,userName:'',userId:''}
default:
return state
}
}
這裏使用了ES6的擴展運算符(...)創建新的state對象,避免直接修改之前的state對象。
在實際項目中,可能會有很多reducer,就需要把reducer拆分保存在單獨的文件中。Redux提供了一個combineReducers函數,用來合併reducer。
import {combineReducers} from 'redux';
import pageHeaderReducer from './pageHeader.js';
import userReducer from './userReducer';
const appReducer = combineReducers({
pageHeaderReducer,
userReducer,
});
export default appReducer;
store
store是Redux中的一個對象,作爲action和reducer之間的一個橋樑。
作用:
- 保存應用狀態
- 通過方法獲取應用狀態
- 通過dispatch(action)發送更新狀態的指令
- 通過方法subscribe(listener)註冊監聽函數、監聽狀態的改變。
總結下Redux中的數據流過程:
- (1)通過調用store.dispatch(action)。一個action描述了“發生了什麼”的對象以及可能會攜帶一些參數。store.dispatch(action)可以在應用中任何位置調用。
- (2)Redux的store調用reducer函數。store傳遞兩個參數給reducer,分別是當前應用的狀態和action,reducer必須是純函數。
- (3)根reducer可以把多個子reducer合併在一起,返回組成最終的應用狀態。利用combineReducers進行組合。
- (4)Redux的store保存根reducer返回的完整應用狀態,這整個流程走完,應用狀態才完成更新。
在React中使用Redux
基礎安裝使用方法已經在第一篇中做了介紹,此處重點介紹下connect中的mapStateToProps和mapDispatchToProps。
mapStateToProps
mapStateToProps是一個函數,從名字上看,該函數作用就是把state轉換成props。state就是Redux store中保存的應用狀態,會作爲參數傳遞給mapStateToProps,props就是被連接展示組件的props。
const mapStateToProps = (state) => {
return {
userName:state.userReducer.userName,
userImage:state.userReducer.userImage,
userId:state.userReducer.userId
}
}
當store中的state改變後,mapStateToProps就會重新執行,重新計算傳遞給展示組件的props,從而觸發組件的重新渲染。
注意:store中的state改變一定會導致mapStateToProps重新執行,但卻不一定會觸發組件渲染render方法重新執行。如果mapStateToProps新返回的對象和之前的對象淺比較相等,組件的shouldComponentUpdate方法就會返回false,組件的render方法就不會被再次觸發,這也是一個重要優化吧!
mapDispatchToProps
mapDispatchToProps接收store.dispatch方法作爲參數,返回展示組件用來修改state的函數。
const mapDispatchToProps = (dispatch, ownProps) => {
return {
getOrder: (data) => dispatch(actionCreator(data))
}
}
另外一種寫法,利用bindActionCreators
import { bindActionCreators } from "redux";
const mapDispatchToProps = (dispatch, ownProps) => {
return bindActionCreators({
getOrder: actionCreator.getOrder
},dispatch);
}
bindActionCreators作用是將單個或多個ActionCreator轉化爲dispatch(action)的函數集合形式。個人感覺會內部自動注入dispatch,不用我們手動去dispatch。之後通過connect連接即可
const mapStateToProps = (state, ownProps) => {
return {
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return bindActionCreators({
login: authActions.login
},dispatch);
}
Login=connect(mapStateToProps,mapDispatchToProps)(Login)
注意connect函數參數必須第一個爲mapStateToProps,第二個必須爲mapDispatchToProps,可以不寫mapDispatchToProps,但是如果需要mapDispatchToProps卻不能不寫mapStateToProps,否則會報錯,個人第一次使用就犯了這個錯誤😓
囉哩囉嗦的說了這麼多,碼字不易,點贊再走哈。完整項目代碼在github,歡迎點個star,不勝感激👏👏👏