Action
Action 是把数据从应用(服务器响应,用户输入或其它非 view 的数据 )传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过 store.dispatch() 将 action 传到 store。分下边两类
/*
* action 常量
*/
export const ADD_TODO = 'ADD_TODO';
export const VisibilityFilters = {
SHOW_ALL: 'SHOW_ALL',
SHOW_COMPLETED: 'SHOW_COMPLETED',
}
/*
* action 创建函数
*/
export function addTodo(text) {
return { type: ADD_TODO, text }
}
export const addTodo = (id)=>{
return {
type: EDITORUSERID,
id:id
}
}
reducer
是一个纯函数,接收旧的 state 和 action,返回新的 state。
(previousState, action) => newState
注意:永远不要在 reducer 里做这些操作:
- 修改传入参数;
- 执行有副作用的操作,如 API 请求和路由跳转;
- 调用非纯函数,如 Date.now() 或 Math.random()。
function todos(state = [], action) {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
text: action.text,
completed: false
}
]
default:
return state
}
}
combineReducers管理多个Reducer
const todoApp = combineReducers({
visibilityFilter,
todos
})
export default todoApp
当你触发 action 后,combineReducers 返回的 todoApp 会负责调用两个 reducer:
let nextTodos = todos(state.todos, action)
注意:也可以 reducer 放到一个独立的文件中,通过 export 暴露出每个 reducer 函数import * as reducers from './reducers'
Store
Store 有以下职责:
- 维持应用的 state;
- 提供 getState() 方法获取 state;
- 提供 dispatch(action) 方法更新 state;
- 通过 subscribe(listener) 注册监听器;
- 通过 subscribe(listener) 返回的函数注销监听器
import todoApp from './reducers'
let store = createStore(todoApp)
createStore() 的第二个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。
let store = createStore(todoApp, window.STATE_FROM_SERVER)
容器组件
Redux 的 React 绑定库是基于 容器组件和zhan shi组件相分离 的开发思想
展示组件 | 容器组件 | |
---|---|---|
作用 | 描述如何展现(骨架、样式) | 描述如何运行(数据获取、状态更新) |
直接使用 Redux | 否 | 是 |
数据来源 | props | 监听 Redux state |
数据修改 | 从 props 调用回调函数 | 向 Redux 派发 actions |
调用方式 | 手动 | 通常由 React Redux 生成 |
展示组件就是一般的js文件容器组件往往使用connect(mapStateToProps,mapDispatchToProps)
创建。
mapStateToProps是把容器组件state向展示组件props映射。mapDispatchToProps() 是映射回调方法。例如,我们希望 VisibleTodoList 向 TodoList 组件中注入一个叫 mOnClick 的 props ,还希望 onTodoClick 能分发 increaseAction 这个 action:
const App=connect(
(state)=>({
value:state.count
}),(dispatch)=>({
mOnClick:()=>dispatch(increaseAction)
})
)(Counter);
Provider
所有容器组件都可以访问 Redux store,建议的方式是使用指定的 React Redux 组件
let store = createStore(todoApp)
render(
,
document.getElementById('root')
)
//创建组件的简单写法
const App = () => (
)
export default App
高级部分(处理异步Action)
标准的做法是使用 Redux Thunk
中间件。
action 创建函数除了返回 action 对象外还可以返回函数。这时,这个 action 创建函数就成为了 thunk。这个函数会被 Redux Thunk middleware 执行。
我们仍可以在 actions.js 里定义这些特殊的 thunk action 创建函数。
创建thunk action
//thunk action
// 虽然内部操作不同,你可以像其它 action 创建函数 一样使用它:
// store.dispatch(fetchPosts('reactjs'))
export function fetchPosts(subreddit) {
return function (dispatch) {
// 首次 dispatch:更新应用的 state 来通知
// API 请求发起了。
dispatch(requestPosts(subreddit))
return fetch(`http://www.subreddit.com/r/${subreddit}.json`)
.then(
response => response.json(),
error => console.log('An error occurred.', error)
)
.then(json =>
dispatch(receivePosts(subreddit, json))
)
}
}
export function fetchPostsIfNeeded(subreddit) {
// 当缓存的值是可用时,
// 减少网络请求很有用。
return (dispatch, getState) => {
if (shouldFetchPosts(getState(), subreddit)) {
// 在 thunk 里 dispatch 另一个 thunk!
return dispatch(fetchPosts(subreddit))
} else {
// 告诉调用代码不需要再等待。
return Promise.resolve()
}
}
}
middleware
你可以利用 Redux middleware 来进行日志记录、创建崩溃报告、调用异步接口或者路由等等。应用中间件要改造下createStore()
* 记录所有被发起的 action 以及产生的新的 state。
*/
const logger = store => next => action => {
console.group(action.type)
let result = next(action)
console.log('next state', store.getState())
return result
}
let store = createStore(
todoApp,
applyMiddleware(
logger
)
优化减少模版代码
1 action优化
1.1 你可以写一个用于生成 action creator 的函数:
function makeActionCreator(type, ...argNames) {
return function(...args) {
let action = { type }
argNames.forEach((arg, index) => {
action[argNames[index]] = args[index]
})
return action
}
}
const ADD_TODO = 'ADD_TODO'
const EDIT_TODO = 'EDIT_TODO'
export const addTodo = makeActionCreator(ADD_TODO, 'todo')
export const editTodo = makeActionCreator(EDIT_TODO, 'id', 'todo')
1.2异步 Action Creators
export function loadPosts(userId) {
return {
// 要在之前和之后发送的 action types
types: ['LOAD_POSTS_REQUEST', 'LOAD_POSTS_SUCCESS', 'LOAD_POSTS_FAILURE'],
// 检查缓存 (可选):
shouldCallAPI: (state) => !state.users[userId],
// 进行取:
callAPI: () => fetch(`http://myapi.com/users/${userId}/posts`),
// 在 actions 的开始和结束注入的参数
payload: { userId }
};
}
2.reducer重构
方法抽取
function addTodo(state, action) {
...
return updateObject(state, {todos : newTodos});
}
function todoReducer(state = initialState, action) {
switch(action.type) {
case 'SET_VISIBILITY_FILTER' : return setVisibilityFilter(state, action);
case 'ADD_TODO' : return addTodo(state, action);
default : return state;
}
}
善用combineReducers
函数
// 使用 ES6 的对象字面量简写方式定义对象结构
const rootReducer = combineReducers({
todoReducer,
firstNamedReducer
});
const store = createStore(rootReducer);
3.大多数应用会处理多种数据类型,通常可以分为以下三类:
- 域数据(Domain data): 应用需要展示、使用或者修改的数据(比如 从服务器检索到的所有 todos
- 应用状态(App state): 特定于应用某个行为的数据(比如 “Todo #5 是现在选择的状态”,或者 “正在进行一个获取 Todos 的请求”)
- UI 状态(UI state): 控制 UI 如何展示的数据(比如 “编写 TODO 模型的弹窗现在是展开的”)
一个典型的应用 state 大致会长这样:
{
domainData1 : {},
domainData2 : {},
appState1 : {},
appState2 : {},
ui : {
uiState1 : {},
uiState2 : {},
}
}
必要时可采用
Redux-ORM
参考
github redux
Redux 中文文档
如有疏漏,请指出,如有问题可以通过如下方式联系我
csdn
掘金
klvens跑码场