做者:赵玮龙 前后就任于面包旅行,阿里体育,如今就任于美团。涉及技术范围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