【问题标题】:React Redux, how to handle form item creation and redirects?React Redux,如何处理表单项的创建和重定向?
【发布时间】:2018-11-27 08:26:33
【问题描述】:

我是 React(和 Redux)的新手,并尝试制作一个简单的表单来创建和 Item (name, type),并重定向到ListIems 页面,创建后。

我正在使用 react-thunk 来处理异步操作,一切都在 typescript 中。

我的问题是如何处理创建结果和重定向?由于我的操作 CreateItemRequest 是异步的,我如何通知我的容器项目已成功创建,以便它重定向到 ListIems 页面。

我尝试在我的减速器的CreateItemSuccess 操作中简单地使用布尔值,但是,我不能去创建表单本身,因为布尔值现在是模型,然后立即重定向到ListIems 页面。

我被困在这里,我确定我错过了一些愚蠢的东西,但在任何地方都找不到任何东西......


我的组件:

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

【问题讨论】:

    标签: reactjs typescript react-redux


    【解决方案1】:

    当您调度异步操作时,您会在未来某个不可预测的时间更新存储的状态。您的组件必须通过mapStateToProps 以某种方式接收这些更新。

    通过这种方式,您可以处理用于创建新项目的道具得到更新 - 大概最好在 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;
    }
    

    Here are the docsgetSnapshotBeforeUpdate

    【讨论】:

    • 我怎么知道这不是来自另一个动作的更新,比如LoadTypes
    • 事实上,似乎 componentWillReceiveProps 已被弃用。请改用 getSnapshotBeforeUpdate。您会收到以前的道具,因此您可以检查某些道具是否发生了变化。
    • 我遇到了和以前一样的问题,当我再次导航到这个组件时,getSnapshotBeforeUpdate 被调用,并且道具已经改变(false => true),所以重定向是立即进行的.
    • 确保设置正确的默认道具/初始状态,并在您离开后在商店中重置它,以便您以干净的状态返回
    • 当我使用 Redux 时,我只能使用操作来更新状态,对吗?
    【解决方案2】:

    您可以使用Promiseasync/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 */
      });
    }
    

    【讨论】:

    • 这是我以前见过的东西,但是 Redux 或 React-Redux 打字稿定义,并没有给我一个 Promise 以作为 Dispatch 的回报......即使它实际上是一个 Promise,正如我用any 测试过的类型:CreateItem: (model: Actions.IItemCreateModel) =&gt; void; 变成CreateItem: (model: Actions.IItemCreateModel) =&gt; any;,我不能按照你的建议执行 async/then。但是由于它不在 Typescript 定义中,所以我认为这不是一个好的行为?
    • 我对 ts 不熟悉,但我认为肯定可以在那里使用 Promises。您应该找到 ITEM_CREATE_CHANGE 函数并按照我显示的方式使用它
    • 我使用它的方式与@SashaKos 已经提到的相同。 (没有 ts)。我的动作创建者主要是 API 调用,它们返回 Promises。所以很容易在成功或错误时执行某些操作。
    • 我想我明白了,但我想保留recommended async flow for redux。我觉得在我的控制器中等待 Promise 不会尊重这个原则并且可能会产生一些奇怪的行为?
    • 这是第一件事,我想给你建议,但你的任务是把它移到 redux 上。但是如果你仍然想使用 redux,你可以添加一些字段,我通常会调用 pending。最初它为空,​​当您开始提交时 - 您将其设置为 true,提交完成时 - 设置为 true。然后在组件(componentWillReceiveNewProps)中 - 当newProps.pending === true -> 导航
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-14
    • 2020-02-23
    • 2019-07-08
    • 1970-01-01
    • 1970-01-01
    • 2013-02-28
    相关资源
    最近更新 更多