摘要:入門實例前端技術真是日新月異,搞完不搭配個數據流都不好意思了。關于的用法,這只是基礎入門的部分,還有的多的搞基操作,比如異步數據流和配合。
redux —— 入門實例 TodoList
前端技術真是日新月異,搞完 React 不搭配個數據流都不好意思了。
滿懷期待的心去翻了翻 flux,簡直被官方那意識流的文檔折服了,真是又臭又長還是我智商問題??
轉戰 redux ,越看越有意思,跟著文檔做了個 TodoList 的入門小例子。
廢話不多說,先貼上文章用到例子的源碼 https://github.com/TongchengQiu/TodoList-as-redux-demo
redux 的 Github 倉庫 https://github.com/rackt/redux
還有個中文的 gitbook 翻譯文檔 http://camsong.github.io/redux-in-chinese/index.html
隨著spa(不是SPA,是單頁應用)的發展,以 react 來說,組件化和狀態機的思想真是解放了煩惱的 dom 操作,一切都為狀態。state 來操縱 views 的變化。
然而,因為頁面的組件化,導致每個組件都必須維護自身的一套狀態,對于小型應用還好。
但是對于比較大的應用來說,過多的狀態顯得錯綜復雜,到最后難以維護,很難清晰地組織所有的狀態,在多人開發中也是如此,導致經常會出現一些不明所以的變化,越到后面調試上也是越麻煩,很多時候 state 的變化已經不受控制。對于組件間通行、服務端渲染、路由跳轉、更新調試,我們很需要一套機制來清晰的組織整個應用的狀態,redux 應然而生,這種數據流的思想真是了不起。
在 react 中,我們盡量會把狀態放在頂層的組件,在頂層組件使用 redux 或者 router。
這就把組件分為了兩種:容器組件和展示組件。
容器組件:和 redux 和 router 交互,維護一套狀態和觸發 action。
展示組件:展示組件是在容器組件的內部,他們不維護狀態,所有數據通過 props 傳給他們,所有操作也是通過回調完成。
這樣,我們整套應用的架構就顯得清晰了。
redux 分為三大部分,store , action ,reducer 。
store整個應用的 state 被儲存在一棵 object tree 中,并且這個 object tree 只存在于唯一一個 store 中。
或者這么說 store 的指責有這些:
維護整個應用的 state
提供 getState() 方法獲取 state;
提供 dispatch(action) 方法更新 state;
通過 subscribe(listener) 注冊監聽器。
這么解釋一下,整個應用的 state 都儲存在 store 中,容器組件可以從 store 中獲取所需要的狀態。
容器組件同時也可以發送給 store 一個 action,告訴他改變某個狀態的值,所以說容器組件只要發送一個指令,就可以叫 store 去 setState,然后 store 的 state 改變,回過來容器組件獲取到的 state 改變,導致 views 的更新。
action 可以理解為一種指令,store 數據的唯一由來就是 action,action 是一個對象,它需要至少一個元素,type,type 是這個指令的唯一標識,其它元素是傳送這個指令的 state 值
{ type: ACTION_TYPE, text: “content”, }
這個指令由組件觸發,然后傳到 reducer。
reduceraction 只是說明了要去做什么,和做這件事情需要的參數值。
具體去改變 store 中的 state 是由 reducer 來做的。
reducer 其實是一個包含 switch 的函數,前面不是說組件觸發的 action 會傳遞到 reducer,reducer 接收這個參數 action,他通過 switch(action.type) 然后做不同操作,前面說了,這個 type 是指令的標識,reducer 根據這個標識來作出不同的操作。
這個操作是什么呢?
reducer 還接收另一個參數 state,這個是舊的 state。從 action 里面還可以獲取到做這個操作需要的 參數值。
這個操作其實就是對原有的 state 和 從 action 中的到的值,來進行操作(結合,刪除,...)然后返回一個 新的 state 到 store。
把前面的語言組織一下,整個操作的數據流其實是這樣的:
store 把整個應用的 state,getState(),dispatch(),subscribe() 傳給頂層容器組件;
容器組件和三個部分交互:
內部的展示組件:容器把狀態分發給各個組件,把 dispatch(操作數據的函數)以回調的形式分發給各個組件;
action:容器獲取 action;
reducer:容器可以調用 dispatch(action),這個上面說了,會以回調的形式給下面的子組件,這樣就可以根據不同的用戶操作,調用不同的 dispatch(action),執行了這個函數之后,就把 action 傳給 reducer,然后看 reducer;
reducer 得到容器組件傳來的 action 之后,根據 action.type 這個參數執行不同操作,他還會接收到 store 里面的原 state,然后把原 state 和 action 對象里面的其它參數進行操作,然后 return 一個新的對象。
reducer return 一個新的對象到 store,store 根據這個新對象,更新應用狀態。
----一個循環 ??
connectRedux 和 React 之間沒有關系,他們并補互相依賴,但是 Redux 和 React 搭配起來簡直完美。
我們可以通過 react-redux 這個庫把他們綁定起來
npm install --save react-redux
react-redux 提供兩個東西 Provider 和 connect。
Provider這個 Provider 其實就是一個中間件,他是在原有 App Container 上面再包一層,他的作用就是接收 store 里面的 store 作為 props,將store放在context里,給下面的connect用的。
connect這個組件才是真正連接 Redux 和 React,他包在我們的容器組件的外一層,他接收上面 Provider 提供的 store 里面的 state 和 dispatch,傳給一個構造函數,返回一個對象,以屬性形式床給我們的容器組件。
實戰 TodoList這個項目使用 webpack 來構建,想要了解 webpack 的配置可以看我的其它兩篇文章:
如何使用webpack—webpack-howto;
webpack-best-practice-最佳實踐-部署生產.
. ├── app #開發目錄 | | | ├──actions #action的文件 | | | ├──components #內部組件 | | | ├──containers #容器組件 | | | ├──reducers #reducer文件 | | | ├──stores #store配置文件 | | | └──index.js #入口文件 | ├── dist #發布目錄 ├── node_modules #包文件夾 ├── .gitignore ├── .jshintrc ├── server.js #本地靜態服務器 ├── webpack.config.js #webpack配置文件 └── package.json
這里,我們只關注我們的 app 開發目錄。
index.js 入口文件import React from "react"; import { render } from "react-dom"; import { Provider } from "react-redux"; import App from "./containers/App"; import configureStore from "./stores/configureStore"; const store = configureStore(); render(, document.getElementById("root") );
這里我們從 react-redux 中獲取了一個 Provider 組件,我們把它渲染到應用的最外層。
他需要一個屬性 store ,他把這個 store 放在context里,給App(connect)用。
app/stores.configureStore.js
import { createStore } from "redux"; import rootReducer from "../reducers"; export default function configureStore(initialState) { const store = createStore(rootReducer, initialState); if (module.hot) { module.hot.accept("../reducers", () => { const nextReducer = require("../reducers"); store.replaceReducer(nextReducer); }); } return store; }
他從 redux 拿到 createStore 這個函數,再獲取到 rootReducer ;
createStore 函數接收兩個參數,(reducer, [initialState]),reducer 毋庸置疑,他需要從 store 獲取 state,以及連接到 reducer 交互。
initialState 是可以自定義的一個初始化 state,可選參數。
module.hot這個可以不用管,這是 webpack 熱加載的處理,你也可以不要他。
containers/App.jsx
import React, { Component, PropTypes } from "react"; import { connect } from "react-redux"; import { addTodo, completeTodo, setVisibilityFilter, VisibilityFilters } from "../actions"; import AddTodo from "../components/AddTodo"; import TodoList from "../components/TodoList"; import Footer from "../components/Footer"; class App extends Component { render() { const { dispatch, visibleTodos, visibilityFilter } = this.props; return (); } } App.propTypes = { visibleTodos: PropTypes.arrayOf(PropTypes.shape({ text: PropTypes.string.isRequired, completed: PropTypes.bool.isRequired })), visibilityFilter: PropTypes.oneOf([ "SHOW_ALL", "SHOW_COMPLETED", "SHOW_ACTIVE" ]).isRequired }; function selectTodos(todos, filter) { switch (filter) { case VisibilityFilters.SHOW_ALL: return todos; case VisibilityFilters.SHOW_COMPLETED: return todos.filter(todo => todo.completed); case VisibilityFilters.SHOW_ACTIVE: return todos.filter(todo => !todo.completed); } } // 這里的 state 是 Connect 的組件的 function select(state) { return { visibleTodos: selectTodos(state.todos, state.visibilityFilter), visibilityFilter: state.visibilityFilter }; } export default connect(select)(App);dispatch(addTodo(text)) } /> dispatch(completeTodo(index))} />
他從 react-redux 獲取 connect 連接組件,通過 connect(select)(App) 連接 store 和 App 容器組件。
select 是一個函數,他能接收到一個 state 參數,這個就是 store 里面的 state,然后通過這個函數的處理,返回一個對象,把對象里面的參數以屬性傳送給 App,以及附帶一個 dispatch。
所以在 App 里面可以:
const { dispatch, visibleTodos, visibilityFilter } = this.props;
所以 App 通過 connect 的到 state 和 dispatch,把 state 傳遞給子組件。
dispatch 這個函數可以接收一個 action 參數,然后就會執行 reducer 里面的操作。
比如:
text => dispatch(addTodo(text))
addTodo(text),這個函數是在 action 里面的到的,可以看 action 的代碼,他其實返回一個 action 對象,所以其實就是dispatch(action) 。
actionapp/actions/index.js
export const ADD_TODO = "ADD_TODO"; export const COMPLETE_TODO = "COMPLETE_TODO"; export const SET_VISIBILITY_FILTER = "SET_VISIBILITY_FILTER"; export const VisibilityFilters = { SHOW_ALL: "SHOW_ALL", SHOW_COMPLETED: "SHOW_COMPLETED", SHOW_ACTIVE: "SHOW_ACTIVE", }; export function addTodo(text) { return { type: ADD_TODO, text }; } export function completeTodo(index) { return { type: COMPLETE_TODO, index }; } export function setVisibilityFilter(filter) { return { type: SET_VISIBILITY_FILTER, filter }; }
在聲明每一個返回 action 函數的時候,我們需要在頭部聲明這個 action 的 type,以便好組織管理。
每個函數都會返回一個 action 對象,所以在 容器組件里面 調用
text => dispatch(addTodo(text))
就是調用dispatch(action) 。
reducerapp/reducers/visibilityFilter.js
import { SET_VISIBILITY_FILTER, VisibilityFilters } from "../actions"; const { SHOW_ALL } = VisibilityFilters; function visibilityFilter(state = SHOW_ALL, action) { switch (action.type) { case SET_VISIBILITY_FILTER: return action.filter; default: return state; } } export default visibilityFilter;
這里我們從 actions 獲得各個 type 的參數,以便和 action 做好映射對應。
整個函數其實就是執行 switch,根據不同的 action.type,返回不同的對象狀態。
但是如果我們需要 type 很多,比如除了 visibilityFilter,還有 todos,難道要寫一個長長的switch,當然不。
redux 提供一個 combineReducers 輔助函數,把一個由多個不同 reducer 函數作為 value 的 object,合并成一個最終的 reducer 函數,然后就可以對這個 reducer 調用 createStore。
我們把不同的 reducer 放在不同文件下。
app/reducers/todo.js
import { ADD_TODO, COMPLETE_TODO } from "../actions"; function todos(state = [], action) { switch (action.type) { case ADD_TODO: return [ ...state, { text: action.text, completed: false } ]; case COMPLETE_TODO: return [ ...state.slice(0, action.index), Object.assign({}, state[action.index], { completed: true }), ...state.slice(action.index + 1) ]; default: return state } } export default todos;
然后通過一個 index.js 把他們合并。
app/reducers/index.js
import { combineReducers } from "redux"; import todos from "./todos"; import visibilityFilter from "./visibilityFilter"; const rootReducer = combineReducers({ todos, visibilityFilter }); export default rootReducer;展示組件
app/components/AddTodo/index.jsx
import React, { Component, PropTypes } from "react"; import { findDOMNode } from "react-dom"; export default class AddTodo extends Component { render() { return (); } handleClick(e) { const inputNode = findDOMNode(this.refs.input); const text = inputNode.value.trim(); this.props.onAddClick(text); inputNode.value = ""; } } AddTodo.propTypes = { onAddClick: PropTypes.func.isRequired };
app/components/Todo/index.jsx
import React, { Component, PropTypes } from "react"; export default class Todo extends Component { render() { const { onClick, completed, text } = this.props; return (
app/components/TodoList/index.jsx
import React, { Component, PropTypes } from "react"; import Todo from "../Todo"; export default class TodoList extends Component { render() { return (
app/components/Footer/index.jsx
import React, { Component, PropTypes } from "react"; export default class Footer extends Component { renderFilter(filter, name) { if(filter == this.props.filter) { return name; } return ( { e.preventDefault(); this.props.onFilterChange(filter); }}> {name} ); } render() { return (SHOW {" "} {this.renderFilter("SHOW_ALL", "All")} {", "} {this.renderFilter("SHOW_COMPLETED", "Completed")} {", "} {this.renderFilter("SHOW_ACTIVE", "Active")} .
); } } Footer.propTypes = { onFilterChange: PropTypes.func.isRequired, filter: PropTypes.oneOf([ "SHOW_ALL", "SHOW_COMPLETED", "SHOW_ACTIVE" ]).isRequired };
可以看出,所有的展示組件需要的 state 和 數據,都從屬性中獲取的,所有的操作,都是通過容器組件給的回調函數來操作的。
他們盡可能地不擁有自己的狀態,做無狀態組件。
關于 redux 的用法,這只是基礎入門的部分,還有的多的搞基操作,比如異步數據流、Middleware、和 router 配合。
敬請期待~~~~
???????????????????
我的博客原文redux 大法好 —— 入門實例 TodoList;
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/78341.html
摘要:而函數式編程就不一樣了,這是模仿我們人類的思維方式發明出來的。數據流在中,數據的流動是單向的,即從父節點傳遞到子節點。數據流嚴格的單向數據流是架構的設計核心。 前言 總括: 本文采用react+redux+react-router+less+es6+webpack,以實現一個簡易備忘錄(todolist)為例盡可能全面的講述使用react全家桶實現一個完整應用的過程。 代碼地址:Re...
摘要:首先聲明這篇文章是想說明一下最新版本的的新特性帶來的極大的開發體驗提升而不是如何利用開發應用這個特性就是對的支持在的中有說明具體可以參考這里在版本之前我們在開發應用尤其是在配合一類庫的時候經常用到諸如之類的封裝而這些函數其實都可以用裝飾器的 首先聲明, 這篇文章是想說明一下最新版本的 TypeScript(3.0) 的新特性帶來的極大的 React 開發體驗提升. 而不是如何利用 Ty...
摘要:用于簡單可擴展的狀態管理,相比有更高的靈活性,文檔參考中文文檔,本文作為入門,介紹一個簡單的項目。任務已完成下一個任務修復谷歌瀏覽器頁面顯示問題提交意見反饋代碼創建在中引入主入口文件設置參考入門學習總結 MobX用于簡單、可擴展的React狀態管理,相比Redux有更高的靈活性,文檔參考:MobX中文文檔,本文作為入門,介紹一個簡單的TodoList項目。 1. 預期效果 showIm...
摘要:描述了如何把轉變成下一個。唯一的要點是當變化時需要返回全新的對象,而不是修改傳入的參數。以上是純的使用,使用起來比較雞肋,大量被使用在項目中,封裝庫提供的和可以將和完美結合,使用非常方便。 @subject: wepy-redux-time-todo @author: leinov @date:2018-10-30 wepy-redux-time-todo showImg(ht...
閱讀 3466·2019-08-30 13:15
閱讀 1403·2019-08-29 18:34
閱讀 829·2019-08-29 15:18
閱讀 3488·2019-08-29 11:21
閱讀 3252·2019-08-29 10:55
閱讀 3705·2019-08-26 10:36
閱讀 1874·2019-08-23 18:37
閱讀 1827·2019-08-23 16:57