<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>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 計算衍生數據 [Reselect](https://github.com/faassen/reselect.git) 庫可以創建可記憶的(Memoized)、可組合的 **selector** 函數。Reselect selectors 可以用來高效地計算 Redux store 里的衍生數據。 ### 可記憶的 Selectors 初衷 首先訪問 [Todos 列表示例](../basics/UsageWithReact.md): #### `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 ``` 上面的示例中,`mapStateToProps` 調用了 `getVisibleTodos` 來計算 `todos`。運行沒問題,但有一個缺點:每當組件更新時都會重新計算 `todos`。如果 state tree 非常大,或者計算量非常大,每次更新都重新計算可能會帶來性能問題。Reselect 能幫你省去這些沒必要的重新計算。 ### 創建可記憶的 Selector 我們需要一個可記憶的 selector 來替代這個 `getVisibleTodos`,只在 `state.todos` or `state.visibilityFilter` 變化時重新計算 `todos`,而在其它部分(非相關)變化時不做計算。 Reselect 提供 `createSelector` 函數來創建可記憶的 selector。`createSelector` 接收一個 input-selectors 數組和一個轉換函數作為參數。如果 state tree 的改變會引起 input-selector 值變化,那么 selector 會調用轉換函數,傳入 input-selectors 作為參數,并返回結果。如果 input-selectors 的值和前一次的一樣,它將會直接返回前一次計算的數據,而不會再調用一次轉換函數。 定義一個可記憶的 selector `getVisibleTodos` 來替代上面的無記憶版本: #### `selectors/index.js` ```js import { createSelector } from 'reselect' const getVisibilityFilter = state => state.visibilityFilter const getTodos = state => state.todos export const getVisibleTodos = createSelector( [getVisibilityFilter, getTodos], (visibilityFilter, todos) => { switch (visibilityFilter) { case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filter(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) } } ) ``` 在上例中,`getVisibilityFilter` 和 `getTodos` 是 input-selector。因為他們并不轉換數據,所以被創建成普通的非記憶的 selector 函數。但是,`getVisibleTodos` 是一個可記憶的 selector。他接收 `getVisibilityFilter` 和 `getTodos` 為 input-selector,還有一個轉換函數來計算過濾的 todos 列表。 ### 組合 Selector 可記憶的 selector 自身可以作為其它可記憶的 selector 的 input-selector。下面的 `getVisibleTodos` 被當作另一個 selector 的 input-selector,來進一步通過關鍵字(keyword)過濾 todos。 ```js const getKeyword = state => state.keyword const getVisibleTodosFilteredByKeyword = createSelector( [getVisibleTodos, getKeyword], (visibleTodos, keyword) => visibleTodos.filter(todo => todo.text.indexOf(keyword) > -1) ) ``` ### 連接 Selector 和 Redux Store 如果你在使用 React Redux,你可以在 `mapStateToProps()` 中當正常函數來調用 selectors #### `containers/VisibleTodoList.js` ```js import { connect } from 'react-redux' import { toggleTodo } from '../actions' import TodoList from '../components/TodoList' import { getVisibleTodos } from '../selectors' const mapStateToProps = state => { return { todos: getVisibleTodos(state) } } const mapDispatchToProps = dispatch => { return { onTodoClick: id => { dispatch(toggleTodo(id)) } } } const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList) export default VisibleTodoList ``` ### 在 selectors 中訪問 React Props 到目前為止,我們只看到 selector 接收 Redux store state 作為參數,然而,selector 也可以接收 props。 例如,我們來實現包含多個 Todo List 的應用。我們需要改寫 state 來支持多個 Todo List,每個 Todo List 分別有各自的 `todos` 和 `visibilityFilter` state。 我們還需要改寫 reducer,現在 `todos` 和 `visibilityFilter` 分別在各自的 Todo List state 里, 所以我們只需要一個 `todoLists` reducer 來進行我們的 state 管理。 #### `reducers/index.js` ```js import { combineReducers } from 'redux' import todoLists from './todoLists' export default combineReducers({ todoLists }) ``` #### `reducers/todoLists.js` ```js // Note that we're hard coding three lists here just as an example. // In the real world, we'd have a feature to add/remove lists, // and this would be empty initially. const initialState = { 1: { todos: [], visibilityFilter: 'SHOW_ALL' }, 2: { todos: [], visibilityFilter: 'SHOW_ALL' }, 3: { todos: [], visibilityFilter: 'SHOW_ALL' } } const addTodo = (state, action) => { const todoList = state[action.listId] const { todos } = todoList return { ...state, [action.listId]: { ...todoList, todos: [ ...todos, { id: action.id, text: action.text, completed: false } ] } } } const toggleTodo = (state, action) => { const todoList = state[action.listId] const { todos } = todoList return { ...state, [action.listId]: { ...todoList, todos: todos.map(todo => (todo.id === action.id) ? {...todo, completed: !todo.completed} : todo ) } } } const setVisibilityFilter = (state, action) => { const todoList = state[action.listId] return { ...state, [action.listId]: { ...todoList, visibilityFilter: action.filter } } } export default const todoLists = (state = initialState, action) => { // make sure a list with the given id exists if (!state[action.listId]) { return state; } switch (action.type) { case 'ADD_TODO': return addTodo(state, action) case 'TOGGLE_TODO': return toggleTodo(state, action) case 'SET_VISIBILITY_FILTER': return setVisibilityFilter(state, action) default: return state } } ``` 上面的例子中,我們使用 `todoLists` reducer 來處理全部三個 action, 所以我們需要向 action creator 傳入一個 `listId` 參數 #### `actions/index.js` ```js let nextTodoId = 0 export const addTodo = (text, listId) => ({ type: 'ADD_TODO', id: nextTodoId++, text, listId }) export const setVisibilityFilter = (filter, listId) => ({ type: 'SET_VISIBILITY_FILTER', filter, listId }) export const toggleTodo = (id, listId) => ({ type: 'TOGGLE_TODO', id, listId }) export const VisibilityFilters = { SHOW_ALL: 'SHOW_ALL', SHOW_COMPLETED: 'SHOW_COMPLETED', SHOW_ACTIVE: 'SHOW_ACTIVE' } ``` #### `components/TodoList.js` ```js import React from 'react' import PropTypes from 'prop-types' import Todo from './Todo' const TodoList = ({ todos, toggleTodo, listId }) => ( <ul> {todos.map(todo => ( <Todo key={todo.id} {...todo} onClick={() => toggleTodo(todo.id, listId)} /> ))} </ul> ) export default TodoList ``` 以下是渲染三個 `VisibleTodoList` components 的 `App` , 每個`VisibleTodoList` 都有一個 `listId` prop。 #### 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> <VisibleTodoList listId="1" /> <VisibleTodoList listId="2" /> <VisibleTodoList listId="3" /> </div> ) ``` 每個 `VisibleTodoList` 容器根據 `listId` props 的值選擇不同的 state 切片,讓我們修改 `getVisibilityFilter` 和 `getTodos` 來接收 props。 #### `selectors/todoSelectors.js` ```js import { createSelector } from 'reselect' const getVisibilityFilter = (state, props) => state.todoLists[props.listId].visibilityFilter const getTodos = (state, props) => state.todoLists[props.listId].todos const getVisibleTodos = createSelector( [getVisibilityFilter, getTodos], (visibilityFilter, todos) => { switch (visibilityFilter) { case 'SHOW_COMPLETED': return todos.filter(todo => todo.completed) case 'SHOW_ACTIVE': return todos.filter(todo => !todo.completed) default: return todos } } ) export default getVisibleTodos ``` `props` 可以通過 `mapStateToProps` 傳遞給 `getVisibleTodos`: ```js const mapStateToProps = (state, props) => { return { todos: getVisibleTodos(state, props) } } ``` 現在,`getVisibleTodos` 可以訪問 `props`,一切看上去都是如此的美好。 **但是這兒有一個問題!** 使用帶有多個 `visibleTodoList` 容器實例的 `getVisibleTodos` selector 不能正常使用函數記憶功能。 #### containers/VisibleTodoList.js ```js import { connect } from 'react-redux' import { toggleTodo } from '../actions' import TodoList from '../components/TodoList' import { getVisibleTodos } from '../selectors' const mapStateToProps = (state, props) => { return { // 警告:下面的 selector 不會正確記憶 todos: getVisibleTodos(state, props) } } const mapDispatchToProps = dispatch => { return { onTodoClick: id => { dispatch(toggleTodo(id)) } } } const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList) export default VisibleTodoList ``` 用 `createSelector` 創建的 selector 只有在參數集與之前的參數集相同時才會返回緩存的值。如果我們交替的渲染 `VisibleTodoList listId="1" />` 和 `VisibleTodoList listId="2" />`,共享的 selector 將交替的接收 `listId: 1` 和 `listId: 2`。這會導致每次調用時傳入的參數不同,因此 selector 將始終重新計算而不是返回緩存的值。我們將在下一節了解如何解決這個限制。 ### 跨多組件的共享 Selector > 這節中的例子需要 React Redux v4.3.0 或者更高的版本 為了跨越多個 `VisibleTodoList` 組件共享 selector,**同時實現**正確記憶。每個組件的實例需要有拷貝 selector 的私有版本。 我們創建一個 `makeGetVisibleTodos` 的函數,在每個調用的時候返回一個 `getVisibleTodos` selector 的新拷貝。 ####selectors/todoSelectors.js ```js import { createSelector } from 'reselect' const getVisibilityFilter = (state, props) => state.todoLists[props.listId].visibilityFilter const getTodos = (state, props) => state.todoLists[props.listId].todos const makeGetVisibleTodos = () => { return createSelector( [getVisibilityFilter, getTodos], (visibilityFilter, todos) => { switch (visibilityFilter) { case 'SHOW_COMPLETED': return todos.filter(todo => todo.completed) case 'SHOW_ACTIVE': return todos.filter(todo => !todo.completed) default: return todos } } ) } ``` 我們還需要一種每個容器訪問自己私有 selector 的方式。`connect` 的 `mapStateToProps` 函數可以幫助我們。 **如果 `connect` 的 `mapStateToProps` 返回的不是一個對象而是一個函數,他將被用做為每個容器的實例創建一個單獨的 `mapStateToProps` 函數。** 下面例子中的 `makeMapStateToProps` 創建一個新的 `getVisibleTodos` selectors,返回一個獨占新 selector 的權限的 `mapStateToProps` 函數。 ```js const makeMapStateToProps = () => { const getVisibleTodos = makeGetVisibleTodos() const mapStateToProps = (state, props) => { return { todos: getVisibleTodos(state, props) } } return mapStateToProps } ``` 如果我們通過 `makeMapStateToProps` 來 `connect`,`VisibleTodosList` 容器的每個組件都會擁有含私有 `getVisibleTodos` selector 的 `mapStateToProps`。不論 `VisibleTodosList` 容器的展現順序如何,記憶功能都會正常工作。 #### container/VisibleTodosList.js ```js import { connect } from 'react-redux' import { toggleTodo } from '../actions' import TodoList from '../components/TodoList' import { makeGetVisibleTodos } from '../selectors' const makeMapStateToProps = () => { const getVisibleTodos = makeGetVisibleTodos() const mapStateToProps = (state, props) => { return { todos: getVisibleTodos(state, props) } } return mapStateToProps } const mapDispatchToProps = dispatch => { return { onTodoClick: id => { dispatch(toggleTodo(id)) } } } const VisibleTodoList = connect( makeMapStateToProps, mapDispatchToProps )(TodoList) export default VisibleTodoList ``` ### 下一步 查看 [官方文檔](https://github.com/reactjs/reselect) 和 [FAQ](https://github.com/reactjs/reselect#faq)。當因為太多的衍生計算和重復渲染導致出現性能問題時,大多數的 Redux 項目會開始使用 Reselect。所以在你創建一個大型項目的時候確保你對 reselect 是熟悉的。你也可以去研究他的 [源碼](https://github.com/reactjs/reselect/blob/master/src/index.js),這樣你就不認為他是黑魔法了。
                  <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>

                              哎呀哎呀视频在线观看