<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 搭配 React 這里需要再強調一下:Redux 和 React 之間沒有關系。Redux 支持 React、Angular、Ember、jQuery 甚至純 JavaScript。 盡管如此,Redux 還是和 [React](http://facebook.github.io/react/) 和 [Deku](https://github.com/dekujs/deku) 這類庫搭配起來用最好,因為這類庫允許你以 state 函數的形式來描述界面,Redux 通過 action 的形式來發起 state 變化。 下面使用 React 來開發一個 todo 任務管理應用。 ## 安裝 React Redux Redux 默認并不包含 [React 綁定庫](https://github.com/reactjs/react-redux),需要單獨安裝。 ``` npm install --save react-redux ``` 如果你不使用 npm,你也可以從 unpkg 獲取最新的 UMD 包(包括[開發環境包](https://unpkg.com/react-redux@latest/dist/react-redux.js)和[生產環境包](https://unpkg.com/react-redux@latest/dist/react-redux.min.js))。如果你用 `<script>` 標簽的方式引入 UMD 包,那么它會在全局拋出`window.ReactRedux`對象。 ## 容器組件(Smart/Container Components)和展示組件(Dumb/Presentational Components) Redux 的 React 綁定庫是基于 [容器組件和展示組件相分離](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0) 的開發思想。所以建議先讀完這篇文章再回來繼續學習。這個思想非常重要。 已經讀完了?那讓我們再總結一下不同點: <table> <thead> <tr> <th></th> <th scope="col" style="text-align:left">展示組件</th> <th scope="col" style="text-align:left">容器組件</th> </tr> </thead> <tbody> <tr> <th scope="row" style="text-align:right">作用</th> <td>描述如何展現(骨架、樣式)</td> <td>描述如何運行(數據獲取、狀態更新)</td> </tr> <tr> <th scope="row" style="text-align:right">直接使用 Redux</th> <td>否</th> <td>是</th> </tr> <tr> <th scope="row" style="text-align:right">數據來源</th> <td>props</td> <td>監聽 Redux state</td> </tr> <tr> <th scope="row" style="text-align:right">數據修改</th> <td>從 props 調用回調函數</td> <td>向 Redux 派發 actions</td> </tr> <tr> <th scope="row" style="text-align:right">調用方式</th> <td>手動</td> <td>通常由 React Redux 生成</td> </tr> </tbody> </table> 大部分的組件都應該是展示型的,但一般需要少數的幾個容器組件把它們和 Redux store 連接起來。這和下面的設計簡介并不意味著容器組件必須位于組件樹的最頂層。如果一個容器組件變得太復雜(例如,它有大量的嵌套組件以及傳遞數不盡的回調函數),那么在組件樹中引入另一個容器,就像[FAQ](../faq/ReactRedux.md#react-multiple-components)中提到的那樣 技術上講你可以直接使用 `store.subscribe()` 來編寫容器組件。但不建議這么做的原因是無法使用 React Redux 帶來的性能優化。也因此,不要手寫容器組件,而使用 React Redux 的 `connect()` 方法來生成,后面會詳細介紹。 ## 設計組件層次結構 還記得當初如何 [設計 state 根對象的結構](Reducers.md) 嗎?現在就要定義與它匹配的界面的層次結構。其實這不是 Redux 相關的工作,[React 開發思想](https://facebook.github.io/react/docs/thinking-in-react.html)在這方面解釋的非常棒。 我們的概要設計很簡單。我們想要顯示一個 todo 項的列表。一個 todo 項被點擊后,會增加一條刪除線并標記 completed。我們會顯示用戶新增一個 todo 字段。在 footer 里顯示一個可切換的顯示全部/只顯示 completed 的/只顯示 incompleted 的 todos。 ### 展示組件 以下的這些組件(和它們的 props )就是從這個設計里來的: - **`TodoList`** 用于顯示 todos 列表。 - `todos: Array` 以 `{ text, completed }` 形式顯示的 todo 項數組。 - `onTodoClick(index: number)` 當 todo 項被點擊時調用的回調函數。 - **`Todo`** 一個 todo 項。 - `text: string` 顯示的文本內容。 - `completed: boolean` todo 項是否顯示刪除線。 - `onClick()` 當 todo 項被點擊時調用的回調函數。 - **`Link`** 帶有 callback 回調功能的鏈接 - `onClick()` 當點擊鏈接時會觸發 - **`Footer`** 一個允許用戶改變可見 todo 過濾器的組件。 - **`App`** 根組件,渲染余下的所有內容。 這些組件只定義外觀并不關心數據來源和如何改變。傳入什么就渲染什么。如果你把代碼從 Redux 遷移到別的架構,這些組件可以不做任何改動直接使用。它們并不依賴于 Redux。 ### 容器組件 還需要一些容器組件來把展示組件連接到 Redux。例如,展示型的 `TodoList` 組件需要一個類似 `VisibleTodoList` 的容器來監聽 Redux store 變化并處理如何過濾出要顯示的數據。為了實現狀態過濾,需要實現 `FilterLink` 的容器組件來渲染 `Link` 并在點擊時觸發對應的 action: - **`VisibleTodoList`** 根據當前顯示的狀態來對 todo 列表進行過濾,并渲染 `TodoList`。 - **`FilterLink`** 得到當前過濾器并渲染 `Link`。 - `filter: string` 就是當前過濾的狀態 ### 其它組件 有時很難分清到底該使用容器組件還是展示組件。例如,有時表單和函數嚴重耦合在一起,如這個小的組件: - **`AddTodo`** 含有“Add”按鈕的輸入框 技術上講可以把它分成兩個組件,但一開始就這么做有點早。在一些非常小的組件里混用容器和展示是可以的。當業務變復雜后,如何拆分就很明顯了。所以現在就使用混合型的吧。 ## 組件編碼 終于開始開發組件了!先做展示組件,這樣可以先不考慮 Redux。 ### 實現展示組件 它們只是普通的 React 組件,所以不會詳細解釋。我們會使用函數式無狀態組件除非需要本地 state 或生命周期函數的場景。這并不是說展示組件必須是函數 -- 只是因為這樣做容易些。如果你需要使用本地 state,生命周期方法,或者性能優化,可以將它們轉成 class。 #### `components/Todo.js` ```js import React from 'react' import PropTypes from 'prop-types' const Todo = ({ onClick, completed, text }) => ( <li onClick={onClick} style={{ textDecoration: completed ? 'line-through' : 'none' }} > {text} </li> ) Todo.propTypes = { onClick: PropTypes.func.isRequired, completed: PropTypes.bool.isRequired, text: PropTypes.string.isRequired } export default Todo ``` #### `components/TodoList.js` ```js import React from 'react' import PropTypes from 'prop-types' import Todo from './Todo' const TodoList = ({ todos, onTodoClick }) => ( <ul> {todos.map((todo, index) => ( <Todo key={index} {...todo} onClick={() => onTodoClick(index)} /> ))} </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 ``` #### `components/Link.js` ```js import React from 'react' import PropTypes from 'prop-types' const Link = ({ active, children, onClick }) => { if (active) { return <span>{children}</span> } return ( <a href="" onClick={e => { e.preventDefault() onClick() }} > {children} </a> ) } Link.propTypes = { active: PropTypes.bool.isRequired, children: PropTypes.node.isRequired, onClick: PropTypes.func.isRequired } export default Link ``` #### `components/Footer.js` ```js import React from 'react' import FilterLink from '../containers/FilterLink' const Footer = () => ( <p> Show: <FilterLink filter="SHOW_ALL">All</FilterLink> {', '} <FilterLink filter="SHOW_ACTIVE">Active</FilterLink> {', '} <FilterLink filter="SHOW_COMPLETED">Completed</FilterLink> </p> ) export default Footer ``` ### 實現容器組件 現在來創建一些容器組件把這些展示組件和 Redux 關聯起來。技術上講,容器組件就是使用 [`store.subscribe()`](../api/Store.md#subscribe) 從 Redux state 樹中讀取部分數據,并通過 props 來把這些數據提供給要渲染的組件。你可以手工來開發容器組件,但建議使用 React Redux 庫的 [`connect()`](https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) 方法來生成,這個方法做了性能優化來避免很多不必要的重復渲染。(這樣你就不必為了性能而手動實現 [React 性能優化建議](https://doc.react-china.org/docs/optimizing-performance.html) 中的 `shouldComponentUpdate` 方法。) 使用 `connect()` 前,需要先定義 `mapStateToProps` 這個函數來指定如何把當前 Redux store state 映射到展示組件的 props 中。例如,`VisibleTodoList` 需要計算傳到 `TodoList` 中的 `todos`,所以定義了根據 `state.visibilityFilter` 來過濾 `state.todos` 的方法,并在 `mapStateToProps` 中使用。 ```js 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) } } ``` 除了讀取 state,容器組件還能分發 action。類似的方式,可以定義 `mapDispatchToProps()` 方法接收 [`dispatch()`](../api/Store.md#dispatch) 方法并返回期望注入到展示組件的 props 中的回調方法。例如,我們希望 `VisibleTodoList` 向 `TodoList` 組件中注入一個叫 `onTodoClick` 的 props ,還希望 `onTodoClick` 能分發 `TOGGLE_TODO` 這個 action: ```js const mapDispatchToProps = dispatch => { return { onTodoClick: id => { dispatch(toggleTodo(id)) } } } ``` 最后,使用 `connect()` 創建 `VisibleTodoList`,并傳入這兩個函數。 ```js import { connect } from 'react-redux' const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList) export default VisibleTodoList ``` 這就是 React Redux API 的基礎,但還漏了一些快捷技巧和強大的配置。建議你仔細學習 [它的文檔](https://github.com/reactjs/react-redux)。如果你擔心 `mapStateToProps` 創建新對象太過頻繁,可以學習如何使用 [reselect](https://github.com/reactjs/reselect) 來 [計算衍生數據](../recipes/ComputingDerivedData.md)。 其它容器組件定義如下: #### `containers/FilterLink.js` ```js import { connect } from 'react-redux' import { setVisibilityFilter } from '../actions' import Link from '../components/Link' const mapStateToProps = (state, ownProps) => { return { active: ownProps.filter === state.visibilityFilter } } const mapDispatchToProps = (dispatch, ownProps) => { return { onClick: () => { dispatch(setVisibilityFilter(ownProps.filter)) } } } const FilterLink = connect( mapStateToProps, mapDispatchToProps )(Link) export default FilterLink ``` #### `containers/VisibleTodoList.js` ```js import { connect } from 'react-redux' import { toggleTodo } from '../actions' import TodoList from '../components/TodoList' const getVisibleTodos = (todos, filter) => { switch (filter) { case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filter(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) } } 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 ``` ### 其它組件 #### `containers/AddTodo.js` 回想一下[前面提到的](#其它組件), `AddTodo` 組件的視圖和邏輯混合在一個單獨的定義之中。 ```js import React from 'react' import { connect } from 'react-redux' import { addTodo } from '../actions' let AddTodo = ({ dispatch }) => { let input return ( <div> <form onSubmit={e => { e.preventDefault() if (!input.value.trim()) { return } dispatch(addTodo(input.value)) input.value = '' }} > <input ref={node => { input = node }} /> <button type="submit">Add Todo</button> </form> </div> ) } AddTodo = connect()(AddTodo) export default AddTodo ``` 如果你不熟悉 ref 屬性, 請閱讀這篇[文檔](https://facebook.github.io/react/docs/refs-and-the-dom.html)以熟悉這個屬性的推薦用法。 ### 將容器放到一個組件 #### `components/App.js` ```js import React from 'react' import Footer from './Footer' import AddTodo from '../containers/AddTodo' import VisibleTodoList from '../containers/VisibleTodoList' const App = () => ( <div> <AddTodo /> <VisibleTodoList /> <Footer /> </div> ) export default App ``` ## 傳入 Store 所有容器組件都可以訪問 Redux store,所以可以手動監聽它。一種方式是把它以 props 的形式傳入到所有容器組件中。但這太麻煩了,因為必須要用 `store` 把展示組件包裹一層,僅僅是因為恰好在組件樹中渲染了一個容器組件。 建議的方式是使用指定的 React Redux 組件 [`<Provider>`](https://github.com/reactjs/react-redux/blob/master/docs/api.md#provider-store) 來 [魔法般的](https://doc.react-china.org/docs/context.html) 讓所有容器組件都可以訪問 store,而不必顯式地傳遞它。只需要在渲染根組件時使用即可。 #### `index.js` ```js 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') ) ``` ## 下一步 參照 [本完整示例](ExampleTodoList.md) 來深化理解。然后就可以跳到 [高級教程](../advanced/README.md) 學習網絡請求處理和路由。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看