ReduxはReactとは別のエンティティであり、任意のJavaScriptフロントエンドフレームワークまたはバニラJavaScriptで使用できます。 それでも、ReactとReduxが非常に一般的に一緒に使用されていることは否定できません。このため、 React Redux ライブラリは、2つを非常に簡単に接続できる単純なバインディングを提供します。

ReactReduxバインディングのAPIは非常にシンプルです。アプリ全体でストアにアクセスできるようにするProvider コンポーネントと、ストアから状態を読み取ることができるコンテナーコンポーネントを作成するconnect関数です。とディスパッチアクション。

入門

Reactプロジェクトを初期化し、必要な依存関係があることを確認しましょう。 Create React App を使用して、サンプルのブックマークマネージャーアプリを作成しましょう。

$ npx create-react-app fancy-bookmarks

ここでは、 npx を使用して、最新バージョンのCreateReactAppを使用していることを確認してください。

次に、cdをアプリのディレクトリに追加し、reduxおよびreact-reduxパッケージを追加します。

$ yarn add redux react-redux

# or, using npm:
$ npm install redux react-redux

Reduxのセットアップ

それでは、アプリのReduxセットアップを行いましょう。 ここではあまり説明しませんが、Reduxを初めて使用する場合は、Reduxの概要をご覧ください。

まず、いくつかのアクションタイプ:

アクション/types.js
export const ADD_BOOKMARK = 'ADD_BOOKMARK';
export const DELETE_BOOKMARK = 'DELETE_BOOKMARK';

そして、いくつかのアクションクリエーターは私たちのアクションタイプと一緒に行きます:

アクション/index.js
import uuidv4 from 'uuid/v4';
import { ADD_BOOKMARK, DELETE_BOOKMARK } from './types';

export const addBookmark = ({ title, url }) => ({
  type: ADD_BOOKMARK,
  payload: {
    id: uuidv4(),
    title,
    url
  }
});

export const deleteBookmark = id => ({
  type: DELETE_BOOKMARK,
  payload: {
    id
  }
});

ここでは、uuidライブラリを使用してランダムIDを生成していることに注意してください。


そして、これが私たちのシンプルなアプリに必要な唯一のレデューサーです:

reducer / index.js
import { ADD_BOOKMARK, DELETE_BOOKMARK } from '../actions/types';

export default function bookmarksReducer(state = [], action) {
  switch (action.type) {
    case ADD_BOOKMARK:
      return [...state, action.payload];
    case DELETE_BOOKMARK:
      return state.filter(bookmark => bookmark.id !== action.payload.id);
    default:
      return state;
  }
}

ご覧のとおり、これまでのところ、私たちは純粋にReduxランドにいて、ReactアプリがReduxストアとシームレスに通信できるようにするためにまだ何もしていません。 これが次に取り組むことです。

プロバイダーコンポーネント

ReactReduxのProviderコンポーネントを使用して、メインの App コンポーネントをラップし、Reactコンポーネントツリーの任意のコンテナーコンポーネント(接続されたコンポーネント)からアプリのReduxストアにアクセスできるようにします。

index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './reducers';

import App from './App';

const store = createStore(rootReducer);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

React Routerを使用している場合は、ProviderコンポーネントをBrowserRouterコンポーネントにラップします。

コンテナコンポーネントと接続機能

アプリでReduxストアにアクセスできるようになったので、ストアからの読み取りまたはディスパッチアクションにアクセスできる接続コンポーネントとも呼ばれるいくつかのコンテナーコンポーネントを作成する必要があります。 AddBookmarkBookmarkListの2つのコンテナコンポーネントを作成します。Appコンポーネントはそれらを使用し、次のようになります。

App.js
import React, { Component } from 'react';
import AddBookmark from './containers/AddBookmark';
import BookmarksList from './containers/BookmarksList';

class App extends Component {
  render() {
    return (
      <div>
        <AddBookmark />
        <BookmarksList />
      </div>
    );
  }
}

export default App;

最初のコンテナコンポーネントであるBookmarksListコンポーネントについて説明します。

コンテナ/BookmarksList.js
import React from 'react';
import { connect } from 'react-redux';
import Bookmark from '../components/Bookmark';
import { deleteBookmark } from '../actions';

function BookmarksList({ bookmarks, onDelete }) {
  return (
    <div>
      {bookmarks.map(bookmark => {
        return (
          <Bookmark bookmark={bookmark} onDelete={onDelete} key={bookmark.id} />
        );
      })}
    </div>
  );
}

const mapStateToProps = state => {
  return {
    bookmarks: state
  };
};

const mapDispatchToProps = dispatch => {
  return {
    onDelete: id => {
      dispatch(deleteBookmark(id));
    }
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(BookmarksList);

ReactReduxのconnect関数を使用して、mapStateToProps関数とmapDispatchToProps関数を渡します。 次に、接続したいコンポーネントを使用して、返された関数を呼び出します。

mapStateToPropsは状態の現在の値を受け取り、接続されたコンポーネントへの小道具として状態の一部を利用できるようにするオブジェクトを返す必要があります。 ここでは、状態にはブックマークしかありませんが、複数の状態を異なる小道具にマッピングできるシナリオを想像できます。 例えば:

const mapStateToProps = state => {
  return {
    users: state.users,
    todos: state.todos,
    // ...
  };
};

mapStateToPropsはストアのディスパッチメソッドを受け取り、必要なアクションをストアにディスパッチする小道具としていくつかのコールバックを利用できるようにするオブジェクトを返す必要があります。


次に、 AddBookmark コンポーネント:

コンテナ/AddBookmark.js
import { connect } from 'react-redux';
import { addBookmark } from '../actions';
import NewBookmark from '../components/NewBookmark';

const mapDispatchToProps = dispatch => {
  return {
    onAddBookmark: bookmark => {
      dispatch(addBookmark(bookmark));
    }
  };
};

export default connect(
  null,
  mapDispatchToProps
)(NewBookmark);

このコンポーネントはストアから読み取る必要がないため、接続関数の最初の引数としてnullを渡します。

また、この2番目のコンテナコンポーネントコンポーネントはそれ自体では何もレンダリングせず、代わりにすべてのUIレンダリング部分がNewBookmarkプレゼンテーションコンポーネントに委ねられていることに気付くでしょう。

プレゼンテーションコンポーネント

プレゼンテーションコンポーネントははるかに単純で、ストアに直接アクセスすることはできません。 代わりに、アクションクリエーターを呼び出す状態またはコールバックからの値を持つコンテナーコンポーネントから小道具を受け取ります。 彼らはReduxについて何も知る必要はなく、代わりに彼らに与えられた小道具の機能にすぎません。 プレゼンテーションコンポーネントは、作成が簡単で、再利用が簡単で、テストも簡単です。

たとえば、次のブックマークプレゼンテーションコンポーネントは、1つのブックマークをレンダリングします。

components / Bookmark.js
import React from 'react';

const styles = {
  borderBottom: '2px solid #eee',
  background: '#fafafa',
  margin: '.75rem auto',
  padding: '.6rem 1rem',
  maxWidth: '500px',
  borderRadius: '7px'
};

export default ({ bookmark: { title, url, id }, onDelete }) => {
  return (
    <div style={styles}>
      <h2>{title}</h2>
      <p>URL: {url}</p>
      <button type="button" onClick={() => onDelete(id)}>
        Remove
      </button>
    </div>
  );
};

ご覧のとおり、ブックマークとonDeleteコールバックを小道具として受け取ります。


NewBookmark の場合、これは純粋なプレゼンテーションコンポーネントではなく、入力値のローカル状態を保持するため、よりハイブリッドなコンポーネントです。 それでも、このコンポーネントはReduxをまったく認識していません。

components / NewBookmark.js
import React from 'react';

class NewBookmark extends React.Component {
  state = {
    title: '',
    url: ''
  };

  handleInputChange = e => {
    this.setState({
      [e.target.name]: e.target.value
    });
  };

  handleSubmit = e => {
    e.preventDefault();
    if (this.state.title.trim() && this.state.url.trim()) {
      this.props.onAddBookmark(this.state);
      this.handleReset();
    }
  };

  handleReset = () => {
    this.setState({
      title: '',
      url: ''
    });
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type="text"
          placeholder="title"
          name="title"
          onChange={this.handleInputChange}
          value={this.state.title}
        />
        <input
          type="text"
          placeholder="URL"
          name="url"
          onChange={this.handleInputChange}
          value={this.state.url}
        />
        <hr />
        <button type="submit">Add bookmark</button>
        <button type="button" onClick={this.handleReset}>
          Reset
        </button>
      </form>
    );
  }
}

export default NewBookmark;

そして、それがすべてです! シンプルなブックマークマネージャーアプリが機能しており、ストアからデータを取得してアクションをディスパッチします。

🏇これで、あなたはレースに出かける必要があります! このトピックの別の見方については、公式ドキュメントもご覧ください。