Open Twlig opened 2 years ago
redux和mobx一样也是集中式状态管理。有3个关键概念。
项目的入口文件
import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' import { createStore } from 'redux' import todoApp from './reducers' import App from './components/App' let store = createStore(todoApp) render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
actions/index.js
生成action对象的工厂函数文件。其实不写这个直接在dispatch的时候store.dispatch({type: 'ADD', id: xxx,text:xxx})也可以。
let nextTodoId = 0 export const addTodo = text => { return { type: 'ADD_TODO', id: nextTodoId++, text } } export const setVisibilityFilter = filter => { return { type: 'SET_VISIBILITY_FILTER', filter } } export const toggleTodo = id => { return { type: 'TOGGLE_TODO', id } }
reducers/todos.js
const todos = (state = [], action) => { switch (action.type) { case 'ADD_TODO': return [ ...state, { id: action.id, text: action.text, completed: false } ] case 'TOGGLE_TODO': return state.map(todo => (todo.id === action.id) ? {...todo, completed: !todo.completed} : todo ) default: return state } } export default todos
reducers/visibilityFilter.js
const visibilityFilter = (state = 'SHOW_ALL', action) => { switch (action.type) { case 'SET_VISIBILITY_FILTER': return action.filter default: return state } } export default visibilityFilter
reducers/index.js
combineReducers合并所有reducer。
import { combineReducers } from 'redux' import todos from './todos' import visibilityFilter from './visibilityFilter' const todoApp = combineReducers({ todos, visibilityFilter }) export default todoApp
redux中有有一个重要的概念就是容器组件和UI组件。容器组件负责和操作state,dispatch action。而UI组件被容器组件包裹,容器组件以props的方式给UI组件传递 state和dispatch操作函数。这种分离的做法会让UI组件更加纯粹,能够实现低耦合,高复用。
components/TodoList.js
import React from 'react' import PropTypes from 'prop-types' import Todo from './Todo' const TodoList = ({ todos, onTodoClick }) => ( <ul> {todos.map(todo => ( <Todo key={todo.id} {...todo} onClick={() => onTodoClick(todo.id)} /> ))} </ul> ) TodoList.propTypes = { todos: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.number.isRequired, completed: PropTypes.bool.isRequired, text: PropTypes.string.isRequired }).isRequired ).isRequired, onTodoClick: PropTypes.func.isRequired } export default TodoList
react-rudex是一个用来连接UI组件和容器组件的工具,使用connect方法。
connect(mapstatetoprops, mapdispatchtoprops)(component),返回容器组件。这个方法会把定义的UI组件所需的state属性和定义好的dispatch方法挂载到UI组件的props属性上。UI组件直接通过props属性调用相应的数据和方法。
containers/VisibleTodoList.js
import { connect } from 'react-redux' import { toggleTodo } from '../actions' import TodoList from '../components/TodoList' const getVisibleTodos = (todos, filter) => { switch (filter) { case 'SHOW_COMPLETED': return todos.filter(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) case 'SHOW_ALL': default: return todos } } const mapStateToProps = state => { return { todos: getVisibleTodos(state.todos, state.visibilityFilter) } } const mapDispatchToProps = dispatch => { return { onTodoClick: id => { dispatch(toggleTodo(id)) } } } const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList) export default VisibleTodoList
前面介绍的都是同步示例,redux还可以处理异步数据流。前面介绍过reducer是纯函数,接收相同的输入产生相同的输出,那异步操作肯定不能在reducer中执行。redux设计的方案是在action中执行异步操作,也就是定义异步action,在异步action中还能调用同步action。因此异步action不需要生成action对象,而是返回函数,直接在action中定义异步操作,不需要再经过reducer。
存在的问题:
解决方法:
createStore可以配置第二个参数,applyMiddleware表示启用中间件,让dispatch可以接收action函数
redux-thunk中的thunkMiddleware,会在action返回的函数中自动带上dispatch参数。
actions.js
import fetch from 'cross-fetch' export const REQUEST_POSTS = 'REQUEST_POSTS' function requestPosts(subreddit) { return { type: REQUEST_POSTS, subreddit } } export const RECEIVE_POSTS = 'RECEIVE_POSTS' function receivePosts(subreddit, json) { return { type: RECEIVE_POSTS, subreddit, posts: json.data.children.map(child => child.data), receivedAt: Date.now() } } export const INVALIDATE_SUBREDDIT = ‘INVALIDATE_SUBREDDIT’ export function invalidateSubreddit(subreddit) { return { type: INVALIDATE_SUBREDDIT, subreddit } } // 来看一下我们写的第一个 thunk action 创建函数! // 虽然内部操作不同,你可以像其它 action 创建函数 一样使用它: // store.dispatch(fetchPosts('reactjs')) export function fetchPosts(subreddit) { // Thunk middleware 知道如何处理函数。 // 这里把 dispatch 方法通过参数的形式传给函数, // 以此来让它自己也能 dispatch action。 return function (dispatch) { // 首次 dispatch:更新应用的 state 来通知 // API 请求发起了。 dispatch(requestPosts(subreddit)) // thunk middleware 调用的函数可以有返回值, // 它会被当作 dispatch 方法的返回值传递。 // 这个案例中,我们返回一个等待处理的 promise。 // 这并不是 redux middleware 所必须的,但这对于我们而言很方便。 return fetch(`http://www.subreddit.com/r/${subreddit}.json`) .then( response => response.json(), // 不要使用 catch,因为会捕获 // 在 dispatch 和渲染中出现的任何错误, // 导致 'Unexpected batch number' 错误。 // https://github.com/facebook/react/issues/6895 error => console.log('An error occurred.', error) ) .then(json => // 可以多次 dispatch! // 这里,使用 API 请求结果来更新应用的 state。 dispatch(receivePosts(subreddit, json)) ) } }
index.js
import thunkMiddleware from 'redux-thunk' import { createLogger } from 'redux-logger' import { createStore, applyMiddleware } from 'redux' import { selectSubreddit, fetchPosts } from './actions' import rootReducer from './reducers' const loggerMiddleware = createLogger() const store = createStore( rootReducer, applyMiddleware( thunkMiddleware, // 允许我们 dispatch() 函数 loggerMiddleware // 一个很便捷的 middleware,用来打印 action 日志 ) ) store.dispatch(selectSubreddit('reactjs')) store .dispatch(fetchPosts('reactjs')) .then(() => console.log(store.getState()) )
redux
redux和mobx一样也是集中式状态管理。有3个关键概念。
基础应用
入口文件(index.jsx):
项目的入口文件
创建Action(
actions/index.js
):生成action对象的工厂函数文件。其实不写这个直接在dispatch的时候store.dispatch({type: 'ADD', id: xxx,text:xxx})也可以。
Reducers:
reducers/todos.js
)reducers/visibilityFilter.js
reducers/index.js
combineReducers合并所有reducer。
UI组件
redux中有有一个重要的概念就是容器组件和UI组件。容器组件负责和操作state,dispatch action。而UI组件被容器组件包裹,容器组件以props的方式给UI组件传递 state和dispatch操作函数。这种分离的做法会让UI组件更加纯粹,能够实现低耦合,高复用。
components/TodoList.js
容器组件
react-rudex是一个用来连接UI组件和容器组件的工具,使用connect方法。
connect(mapstatetoprops, mapdispatchtoprops)(component),返回容器组件。这个方法会把定义的UI组件所需的state属性和定义好的dispatch方法挂载到UI组件的props属性上。UI组件直接通过props属性调用相应的数据和方法。
containers/VisibleTodoList.js
异步Action
前面介绍的都是同步示例,redux还可以处理异步数据流。前面介绍过reducer是纯函数,接收相同的输入产生相同的输出,那异步操作肯定不能在reducer中执行。redux设计的方案是在action中执行异步操作,也就是定义异步action,在异步action中还能调用同步action。因此异步action不需要生成action对象,而是返回函数,直接在action中定义异步操作,不需要再经过reducer。
存在的问题:
解决方法:
createStore可以配置第二个参数,applyMiddleware表示启用中间件,让dispatch可以接收action函数
redux-thunk中的thunkMiddleware,会在action返回的函数中自动带上dispatch参数。
actions.js
index.js