做者:趙瑋龍 前後就任於麪包旅行,阿里體育,如今就任於美團。涉及技術範圍React, AngularJS, gulp, grunt, webpack, redux, canvas, node等,如今專一於前端react周邊技術棧研究javascript
文章專著於如何儘可能作到react-redux最佳實踐前端
function increment(state, props) {
return {count: state.count + 1};
}複製代碼
function incrementMultiple() {
this.setState(increment);
this.setState(increment);
this.setState(increment);
}複製代碼
function incrementMultiple() {
this.setState(increment);
this.setState(increment);
this.setState({count: this.state.count + 1});
this.setState(increment);
}複製代碼
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
//普通action
function foo(){
return {
type: 'foo',
data: 123
}
}
//異步action
function fooAsync(){
return dispatch => {
setTimeout(_ => dispatch(123), 3000);
}
}複製代碼
function upload(data){
return dispatch => {
// view
dispatch({ type: 'SHOW_WAITING_MODAL' });
// upload
api.upload(data)
.then(res => {
// 成功
dispatch({ type: 'PRELOAD_IMAGES', data: res.images });
dispatch({ type: 'HIDE_WAITING_MODAL' });
})
.catch(err => {
// 錯誤
dispatch({ type: 'SHOW_ERROR', data: err });
dispatch({ type: 'HIDE_WAITING_MODAL' });
setTimeout(_ => dispatch({ type: 'HIDE_ERROR' }), 2000);
})
}
}複製代碼
import { take, put, call, delay } from 'redux-saga/effects'
// 上傳的異步流
function *uploadFlow(action) {
// 顯示出加載效果
yield put({ type: 'SHOW_WAITING_MODAL' });
// 簡單的 try-catch
try{
const response = yield call(api.upload, action.data);
yield put({ type: 'PRELOAD_IMAGES', data: response.images });
yield put({ type: 'HIDE_WAITING_MODAL' });
}catch(err){
yield put({ type: 'SHOW_ERROR', data: err });
yield put({ type: 'HIDE_WAITING_MODAL' });
yield delay(2000);
yield put({ type: 'HIDE_ERROR' });
}
}
function* watchUpload() {
yield* takeEvery('BEGIN_REQUEST', uploadFlow)
}複製代碼
import { buffers } from 'redux-saga';
import { take, actionChannel, call, ... } from 'redux-saga/effects';
function* watchRequests() {
// 1- 建立一個針對請求事件的 channel
const requestChan = yield actionChannel('REQUEST');
while (true) {
// 2- 從 channel 中拿出一個事件
const {payload} = yield take(requestChan);
// 3- 注意這裏咱們使用的是阻塞的函數調用
yield call(handleRequest, payload);
}
}複製代碼
import 'whatwg-fetch'
import handleError from './handleError'
// 設定一個symbol類型作爲惟一的屬性名
export const CALL_API = Symbol('call_api')
const API_HOST = process.env.API_HOST || 'http://localhost:8080/pc'
export default store => next => action => {
const callApi = action[CALL_API]
if (typeof callApi === 'undefined') {
return next(action)
}
// 獲取action中參數
let { endpoint,
types: [requestType, successType, failureType],
method,
body,
...options
} = callApi
let finalBody = body
if (method) {
options.method = method.toUpperCase()
}
if (typeof body === 'function') {
finalBody = body(store.getState())
}
if (finalBody) {
options.body = JSON.stringify(finalBody)
options.headers = { 'content-type': 'application/json', 'agent': 'pc' }
} else {
options.headers = { 'cache-control': 'no-cache', 'agent': 'pc' }
}
// 替換action標記方法
const actionWith = data => {
const finalAction = Object.assign({}, action, data)
delete finalAction[CALL_API]
return finalAction
}
next(actionWith({ type:requestType }))
return fetch(`${API_HOST}${endpoint}`,{
credentials: 'include',
...options,
})
.then(response => {
if (response.status === 204) {
return { response }
}
const type = response.headers.get('content-type')
if (type && type.split(';')[0] === 'application/json') {
return response.json().then(json => ({ json, response }))
}
return response.text().then(text => ({ text, response }))
})
.then(({ json, text, response }) => {
if (response.ok) {
if (json) {
if (json.status === 200 && json.data) {
next(actionWith({ type: successType, payload: json.data }))
} else if (json.status === 500) {
next(actionWith({ type: successType, payload: json.msg }))
} else {
next(actionWith({ type: successType }))
}
}
} else {
if (json) {
let error = { status: response.status }
if (typeof json === 'object') {
error = { ...error, ...json }
} else {
error.msg = json
}
throw error
}
const error = {
name: 'FETCH_ERROR',
status: response.status,
text,
}
throw error
}
})
.catch((error) => {
next(actionWith({ type: failureType, error }))
handleError(error)
})
}複製代碼
結構狀態state應該如何去設計呢?vue
純淨,和業務邏輯不耦合
功能單一,一個函數只實現一個功能java
// 好比對象更新,淺拷貝
export const updateObject = (oldObj, newObj) => {
return assign({}, oldObj, newObj);
}
// 好比對象更新,深拷貝
export const deepUpdateObject = (oldObj, newObj) => {
return deepAssign({}, oldObj, newObj);
}複製代碼
// 抽離前,全部代碼都揉到slice reducer中,不夠清晰
function appreducer(state = initialState, action) {
switch (action.type) {
case 'ADD_TODO':
...
...
return newState;
case 'TOGGLE_TODO':
...
...
return newState;
default:
return state;
}
}
// 抽離後,將全部的state處理邏輯放到單獨的函數中,reducer的邏輯格外清楚
function addTodo(state, action) {
...
...
return newState;
}
function toggleTodo(state, action) {
...
...
return newState;
}
function appreducer(state = initialState, action) {
switch (action.type) {
case 'ADD_TODO':
return addTodo(state, action);
case 'TOGGLE_TODO':
return toggleTodo(state, action);
default:
return state;
}
}複製代碼
export default (state = initialState, action) => {
switch (action.type) {
case allTypes.SHOPINFO_REQ:
return {
...state,
isloading: true,
}
case allTypes.SHOPINFO_SUCCESS:
return {
...state,
isloading: false,
productId: action.payload.productId,
}
default:
return state
}複製代碼
combineReducers({
entities: entitiesreducer,
// 對於shopReducer來講,他接受(state, action) => newState,
// 其中的state,是shop,也就是state.shopinfo
// 它並不能獲取到state的數據,更不能獲取到state.papers的數據
shopinfo: shopinfoReducer,
bankinfo: bankinfoReducer
})複製代碼
最後,團隊爲了招聘方便,整了個公衆號,主要是一些招聘信息,團隊信息,全部的技術文章在公衆號裏也能夠看到,對了,若是你想去美團其餘團隊,咱們也能夠幫你內推哦 ~ node