国产chinesehdxxxx野外,国产av无码专区亚洲av琪琪,播放男人添女人下边视频,成人国产精品一区二区免费看,chinese丰满人妻videos

Redux 搭配 React

2021-09-16 09:51 更新

搭配 React

這里需要再強調一下:Redux 和 React 之間沒有關系。Redux 支持 React、Angular、Ember、jQuery 甚至純 JavaScript。

盡管如此,Redux 還是和 ReactDeku 這類框架搭配起來用最好,因為這類框架允許你以 state 函數(shù)的形式來描述界面,Redux 通過 action 的形式來發(fā)起 state 變化。

下面使用 React 來開發(fā)一個 todo 任務管理應用。

安裝 React Redux

Redux 默認并不包含 React 綁定庫,需要單獨安裝。

npm install --save react-redux

智能組件(Smart Components)和笨拙組件(Dumb Components)

Redux 的 React 綁定庫擁抱了 “智能”組件和“笨拙”組件相分離 的開發(fā)思想。

明智的做法是只在最頂層組件(如路由操作)里使用 Redux。內部組件應該像木偶一樣保持“呆滯”,所有數(shù)據(jù)都通過 props 傳入。

位置 使用 Redux 讀取數(shù)據(jù) 修改數(shù)據(jù)
“智能”組件 最頂層,路由處理 從 Redux 獲取 state 向 Redux 發(fā)起 actions
“笨拙”組件 中間和子組件 從 props 獲取數(shù)據(jù) 從 props 調用回調函數(shù)

在這個 todo 應用中,只應有一個“智能”組件,它存在于組件的最頂層。在復雜的應用中,也有可能會有多個智能組件。雖然你也可以嵌套使用“智能”組件,但應該盡可能的使用傳遞 props 的形式。

設計組件層次結構

還記得當初如何 設計 reducer 結構 嗎?現(xiàn)在就要定義與它匹配的界面的層次結構。其實這不是 Redux 相關的工作,React 開發(fā)思想在這方面解釋的非常棒。

我們的概要設計很簡單。我們想要顯示一個 todo 項的列表。一個 todo 項被點擊后,會增加一條刪除線并標記 completed。我們會顯示用戶新增一個 todo 字段。在 footer 里顯示一個可切換的顯示全部/只顯示 completed 的/只顯示 incompleted 的 todos。

以下的這些組件(和它們的 props )就是從這個設計里來的:

  • AddTodo 輸入字段的輸入框和按鈕。

  • onAddClick(text: string) 當按鈕被點擊時調用的回調函數(shù)。

  • TodoList 用于顯示 todos 列表。

  • todos: Array{ text, completed } 形式顯示的 todo 項數(shù)組。
  • onTodoClick(index: number) 當 todo 項被點擊時調用的回調函數(shù)。

  • Todo 一個 todo 項。

  • text: string 顯示的文本內容。
  • completed: boolean todo 項是否顯示刪除線。
  • onClick() 當 todo 項被點擊時調用的回調函數(shù)。

  • Footer 一個允許用戶改變可見 todo 過濾器的組件。

  • filter: string 當前的過濾器為: 'SHOW_ALL'、 'SHOW_COMPLETED''SHOW_ACTIVE'。
  • onFilterChange(nextFilter: string): 當用戶選擇不同的過濾器時調用的回調函數(shù)。

這些全部都是“笨拙”的組件。它們不知道數(shù)據(jù)是哪里來的,或者數(shù)據(jù)是怎么變化的。你傳入什么,它們就渲染什么。

如果你要把 Redux 遷移到別的上,你應該要保持這些組件的一致性。因為它們不依賴 Redux。

直接寫就是了!我們已經(jīng)不用綁定到 Redux。你可以在開發(fā)過程中給出一些實驗數(shù)據(jù),直到它們渲染對了。

笨拙組件

這就是普通的 React 組件,所以就不在詳述。直接看代碼:

components/AddTodo.js

import React, { findDOMNode, Component, PropTypes } from 'react';

export default class AddTodo extends Component {
  render() {
    return (
      <div>
        <input type='text' ref='input' />
        <button onClick={e => this.handleClick(e)}>
          Add
        </button>
      </div>
    );
  }

  handleClick(e) {
    const node = findDOMNode(this.refs.input);
    const text = node.value.trim();
    this.props.onAddClick(text);
    node.value = '';
  }
}

AddTodo.propTypes = {
  onAddClick: PropTypes.func.isRequired
};

components/Todo.js

import React, { Component, PropTypes } from 'react';

export default class Todo extends Component {
  render() {
    return (
      <li
        onClick={this.props.onClick}
        style={{
          textDecoration: this.props.completed ? 'line-through' : 'none',
          cursor: this.props.completed ? 'default' : 'pointer'
        }}>
        {this.props.text}
      </li>
    );
  }
}

Todo.propTypes = {
  onClick: PropTypes.func.isRequired,
  text: PropTypes.string.isRequired,
  completed: PropTypes.bool.isRequired
};

components/TodoList.js

import React, { Component, PropTypes } from 'react';
import Todo from './Todo';

export default class TodoList extends Component {
  render() {
    return (
      <ul>
        {this.props.todos.map((todo, index) =>
          <Todo {...todo}
                key={index}
                onClick={() => this.props.onTodoClick(index)} />
        )}
      </ul>
    );
  }
}

TodoList.propTypes = {
  onTodoClick: PropTypes.func.isRequired,
  todos: PropTypes.arrayOf(PropTypes.shape({
    text: PropTypes.string.isRequired,
    completed: PropTypes.bool.isRequired
  }).isRequired).isRequired
};

components/Footer.js

import React, { Component, PropTypes } from 'react';

export default class Footer extends Component {
  renderFilter(filter, name) {
    if (filter === this.props.filter) {
      return name;
    }

    return (
      <a href='#' onClick={e => {
        e.preventDefault();
        this.props.onFilterChange(filter);
      }}>
        {name}
      </a>
    );
  }

  render() {
    return (
      <p>
        Show:
        {' '}
        {this.renderFilter('SHOW_ALL', 'All')}
        {', '}
        {this.renderFilter('SHOW_COMPLETED', 'Completed')}
        {', '}
        {this.renderFilter('SHOW_ACTIVE', 'Active')}
        .
      </p>
    );
  }
}

Footer.propTypes = {
  onFilterChange: PropTypes.func.isRequired,
  filter: PropTypes.oneOf([
    'SHOW_ALL',
    'SHOW_COMPLETED',
    'SHOW_ACTIVE'
  ]).isRequired
};

就這些,現(xiàn)在開發(fā)一個笨拙型的組件 App 把它們渲染出來,驗證下是否工作。

containers/App.js

import React, { Component } from 'react';
import AddTodo from '../components/AddTodo';
import TodoList from '../components/TodoList';
import Footer from '../components/Footer';

export default class App extends Component {
  render() {
    return (
      <div>
        <AddTodo
          onAddClick={text =>
            console.log('add todo', text)
          } />
        <TodoList
          todos={[{
            text: 'Use Redux',
            completed: true
          }, {
            text: 'Learn to connect it to React',
            completed: false
          }]}
          onTodoClick={todo =>
            console.log('todo clicked', todo)
          } />
        <Footer
          filter='SHOW_ALL'
          onFilterChange={filter =>
            console.log('filter change', filter)
          } />
      </div>
    );
  }
}

渲染 <App /> 結果如下:

單獨來看,并沒有什么特別,現(xiàn)在把它和 Redux 連起來。

連接到 Redux

我們需要做出兩個變化,將 App 組件連接到 Redux 并且讓它能夠 dispatch actions 以及從 Redux store 讀取到 state。

首先,我們需要獲取從之前安裝好的 react-redux 提供的 Provider,并且在渲染之前將根組件包裝進 <Provider>。

index.js

import React from 'react';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import App from './containers/App';
import todoApp from './reducers';

let store = createStore(todoApp);

let rootElement = document.getElementById('root');
React.render(
  // 為了解決在 React 0.13 的一個問題
  // 子標簽必須包裝成一個 function。
  <Provider store={store}>
    {() => <App />}
  </Provider>,
  rootElement
);

這使得我們的 store 能為下面的組件所用。(在內部,這個是通過 React 的 "context" 特性實現(xiàn)。)

接著,我們想要通過 react-redux 提供的 connect() 方法將包裝好的組件連接到Redux。盡量只做一個頂層的組件,或者 route 處理。從技術上來說你可以將應用中的任何一個組件 connect() 到 Redux store 中,但盡量要避免這么做,因為這個數(shù)據(jù)流很難追蹤。

任何一個從 connect() 包裝好的組件都可以得到一個 dispatch 方法作為組件的 props。connect() 的唯一參數(shù)是 selector。此方法可以從 Redux store 接收到全局的 state,然后返回一個你的組件中需要的 props。最簡單的情況下,可以返回一個初始的 state ,但你可能希望它發(fā)生了變化。

為了組合 selectors 更有效率,不妨看看 reselect。在這個例子中我們不會用到它,但它適合更大的應用。

containers/App.js

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() {
    // Injected by connect() call:
    const { dispatch, visibleTodos, visibilityFilter } = this.props;
    return (
      <div>
        <AddTodo
          onAddClick={text =>
            dispatch(addTodo(text))
          } />
        <TodoList
          todos={this.props.visibleTodos}
          onTodoClick={index =>
            dispatch(completeTodo(index))
          } />
        <Footer
          filter={visibilityFilter}
          onFilterChange={nextFilter =>
            dispatch(setVisibilityFilter(nextFilter))
          } />
      </div>
    );
  }
}

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);
  }
}

// Which props do we want to inject, given the global state?
// Note: use https://github.com/faassen/reselect for better performance.
function select(state) {
  return {
    visibleTodos: selectTodos(state.todos, state.visibilityFilter),
    visibilityFilter: state.visibilityFilter
  };
}

// Wrap the component to inject dispatch and state into it
export default connect(select)(App);

到此為止,迷你型的任務管理應用就開發(fā)完畢。

下一步

參照 本示例完整 來深化理解。然后就可以跳到 高級教程 學習網(wǎng)絡請求處理和路由。

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號