ウェブサイト検索

reactjs - React Reduxで、フォームアイテムの作成とリダイレクトをどのように扱いますか?


私はReact(およびRedux)に不慣れで、名前とタイプ(name, type)のItemを作成し、作成後にListItemsページにリダイレクトする簡単なフォームを作ろうとしています。

私は非同期アクションを処理するために react-thunk を使用しており、すべては TypeScript で書かれています。

私の問題は、作成結果とリダイレクトをどのように処理するかです。私のアクションCreateItemRequestは非同期であるため、アイテムが正常に作成されたことをコンテナにどのように知らせて、ListItemsページにリダイレクトするかです。

私は単純にブール値を CreateItemSuccess アクションのリデューサで使用しようとしましたが、そうすると、そのブール値がモデルで true になってしまい、すぐに ListItems ページにリダイレクトされるため、作成フォーム自体に移動できません。

ここで行き詰まっています。きっと些細なことを見逃しているんでしょうが、どこを探しても何も見つかりません…

私のコンポーネント:

import { Route, Redirect } from "react-router-dom";
import { connect, Dispatch } from 'react-redux';
import * as React from 'react';

import * as Types from '../../types';
import * as Actions from '../../actions';

interface ICreateModelDispatchProps {
  LoadItems: () => void;
  ChangeForm: (model: Actions.IItemCreateModel) => void;
  CreateItem: (model: Actions.IItemCreateModel) => void;
}

interface ICreateItemStateProps {
  Types: Types.ITypeEntity[];
  ItemCreated: boolean;

  Model: Actions.IItemCreateModel;
}

class CreateItem extends React.Component<ICreateItemStateProps & ICreateModelDispatchProps, Types.IAppState> {
  public componentDidMount() {
    this.props.LoadItems();
  }

  public render() {
    if (this.props.ItemCreated) {
      const redirect = () => <Redirect to={{ pathname: '/items' }} />;
      return <Route render={redirect} />;
    }

    if (!this.props.Types.length) {
      return <div id="create-item" className="loading" />;
    }

    const { Name, Type } = this.props.Model;
    const onSubmit = this.onSubmit.bind(this);
    const onNameChange = this.onNameChange.bind(this);
    const onTypeChange = this.onTypeChange.bind(this);

    return (
      <div id="create-item">
        <form onSubmit={onSubmit}>
          <div>
            <label htmlFor="name">Name</label>
            <input type="text" id="name" value={Name} placeholder="Item's Name" onChange={onNameChange} />
          </div>
          <div>
            <label htmlFor="type">Type</label>
            <select id="type" onChange={onTypeChange} value={Type}>
              <option value="-1">Choose a type</option>
              {this.props.Types.map(type => <option key={type.Id} value={type.Id}>{type.Name}</option>)}
            </select>
          </div>
          <div>
            <input type="submit" value="CREATE" />
          </div>
        </form>
      </div>
    )
  }

  private onNameChange(event: React.ChangeEvent<HTMLInputElement>) {
    this.props.ChangeForm(Object.assign(this.props.Model, { Name: event.target.value }));
  }

  private onTypeChange(event: React.ChangeEvent<HTMLSelectElement>) {
    this.props.ChangeForm(Object.assign(this.props.Model, { Type: event.target.value }));
  }

  private onSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
    this.props.CreateItem(this.props.Model);
  }
}

export function mapStateToProps(state: Types.IAppState): ICreateItemStateProps {
  const types = Object.keys(state.Entities.Types).map(key => state.Entities.Types[Number(key)]);
  const Model = state.CreateItem;

  return {
    Model,
    Types: types,
    ItemCreated: !!state.CreateItem.Created,
  };
}

export function mapDispatchToProps(dispatch: Dispatch): ICreateModelDispatchProps {
  return {
    LoadItems: () => dispatch(Actions.ITEM_TYPES_REQUEST()),
    ChangeForm: (model) => dispatch(Actions.ITEM_CREATE_CHANGE(model)),
    CreateItem: (model) => dispatch(Actions.ITEM_CREATE_REQUEST(model)),
  };
}

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

回答 #1

あなたは自分のアクションを Promise または async/await を使って書くことができます。こちらは js(ts ではない)での例です

export default function ITEM_CREATE_REQUEST(model) {
  return (dispatch) => {
    return new Promise((resolve, reject) => {
      /* SOME STUFF */

      resolve(SOME_DATA);
    });
  }
};

解決されたときに、それをナビゲートに使用することができます

onSubmit() {
  event.preventDefault();
  this.props.CreateItem(this.props.Model).then((SOME_DATA) => {
    /* PERFORM REDIRECT */
  });
}

回答 #2

非同期アクションをディスパッチすると、将来の予測できない時点でストアの状態が更新されます。コンポーネントは、mapStateToProps を通じてこれらの更新を何らかの方法で受け取る必要があります。

こうすることで、新しいアイテムを作成するための props が更新されたことを処理できる - おそらく getSnapshotBeforeUpdate ライフサイクル内で行うのが最適です:

getSnapshotBeforeUpdate(prevProps, prevState) {
    if (prevProps.itemCreated !== this.props.itemCreated) {
        // itemCreated could be a counter or something else.
        // For example, you count up after item is created.
        // Whenever that prop changes, you navigate away like this.
        const { history } = this.props
        history.pushState(null, '/anywhere') or
        history.replaceState(null, '/anywhere')
    }
    return null;
}

こちらは getSnapshotBeforeUpdate のドキュメントです。

関連記事