foxhound87 / mobx-react-form

Reactive MobX Form State Management
https://foxhound87.github.io/mobx-react-form
MIT License
1.1k stars 129 forks source link

routing issues with form.onSubmit #413

Closed betancourtl closed 6 years ago

betancourtl commented 6 years ago

I am using react-router with mobx-react-form, and I running into some issues with the onSubmit function. I need the onSubmit to return a promise so I can route to the next page if the api call is valid. Is there a way for form.onSubmit to return a promise? The docs are really confusing.

Also why does form.onSubmit return an array with 2 empty elements?

betancourtl commented 6 years ago

I ended up doing this but I'm not sure if it is correct.

  handleSubmit = e => {
    e.preventDefault();
    const p1 = new Promise((resolve, reject) => {
      this.onSubmit(e, {
        onSuccess: () => resolve('Ok'),
        onError: () => reject('Failed'),
      });
    });

    return p1
      .then(res => {
        console.log('resolved')
      })
      .catch(err => {
        console.log('rejected')
      });
  };
betancourtl commented 6 years ago

Now I'm running into issues with the isSubmitting prop not waiting for the promise.

betancourtl commented 6 years ago

I managed to get onSubmit to return a promise so I can handle routing on the frontend. The solution I found was to wrap this.onSubmit with a promise. And then call resolve or reject whenever the validations are complete and after my API call to the server is done. I did ran into a small issue where the isSubmitting prop returns true early because it does not take into consideration ongoing promises so I had to manually take care of that by using a computed prop. so far this feels a bit hacky. It would be nice if someone knows how to this work the right way.

Store

import MobxReactForm from 'mobx-react-form';
import validatorjs from 'validatorjs';
import { runInAction, extendObservable, computed } from 'mobx';

const plugins = { dvr: validatorjs };

const fields = [
  {
    name: 'price',
    label: 'Price',
    placeholder: 'Enter price',
    rules: 'required|string|between:1,15',
    value: '',
    type: 'text',
  },
  {
    name: 'date',
    label: 'Date',
    placeholder: 'Enter Date',
    rules: 'required|string',
    value: Date.now(),
    type: 'text'
  },
  {
    name: 'profits',
    label: 'Profits',
    placeholder: 'Profits',
    rules: '',
    value: '',
    type: 'text',
    disabled: true,
  }
];

class BitcoinForm extends MobxReactForm {
  static storeName = 'bitcoinForm';

  constructor() {
    super({ fields }, { plugins });
    extendObservable(this, {
      _submitting: false,
      profits: 0,
    });
  }

  handleSubmit = e => {
    e.preventDefault();
    runInAction(() => this._submitting = true);
    const p1 = new Promise((resolve, reject) => {
      this.onSubmit(e, {
        onSuccess: () => setTimeout(() => resolve('Ok'), 2000),
        onError: () => reject('Failed'),
      });
    });

    return p1
      .then(res => {
        console.log('resolved')
      })
      .catch(err => {
        console.log('rejected')
      })
      .finally(err => {
        runInAction(() => this._submitting = false);
      });
  };

  @computed get isSubmitting() {
    return this.submitting || this._submitting;
  }
}

export default BitcoinForm;

const storeName = BitcoinForm.storeName;

export {
  storeName,
}

Component

import React, { Component } from 'react';
import { observer, inject } from 'mobx-react';
import {
  Row,
  Col,
  Image,
  FormControl,
  FormGroup,
  ControlLabel,
  HelpBlock,
  Button,
} from 'react-bootstrap';
import PropTypes from 'prop-types';

const validationState = (hasError) => {
  return hasError
    ? 'error'
    : 'success'
};

const BitcoinForm = props => {
  const {
    priceInputId,
    priceInputProps,
    priceInputError,
    priceInputHasError,

    dateInputId,
    dateInputProps,
    dateInputError,
    dateInputHasError,

    submit,
    disabled,
  } = props;
  return (
    <form onSubmit={submit}>
      <FormGroup
        controlId={priceInputId}
        validationState={validationState(priceInputHasError)}
      >
        <ControlLabel>Price</ControlLabel>
        <FormControl
          {...priceInputProps}
        />
        <FormControl.Feedback />
        <HelpBlock>{priceInputError}</HelpBlock>
      </FormGroup>

      <FormGroup
        controlId={dateInputId}
        validationState={validationState(dateInputError)}
      >
        <ControlLabel>Date</ControlLabel>
        <FormControl
          {...dateInputProps}
        />
        <FormControl.Feedback />
        <HelpBlock>{dateInputHasError}</HelpBlock>
      </FormGroup>

      <Button
        disabled={disabled}
        bsSize="lg"
        bsStyle="info"
        type="submit"
      >
        Calculate Profits
      </Button>
    </form>
  );
};

@inject('bitcoinForm')
@observer
class Home extends Component {
  render() {
    const form = this.props.bitcoinForm;
    const price = form.$('price');
    const date = form.$('date');
    const priceInputs = price.bind();
    const dateInputs = date.bind();
    const onSubmit = form.handleSubmit;
    const disabled = form.isSubmitting;

    return (
      <Row className="home-page">
        <Col xs={12} sm={6}>
          <Image
            responsive
            src="http://placehold.it/450x300" alt="bitcoin"
          />
        </Col>
        <Col xs={12} sm={6}>
          <BitcoinForm
            priceInputId={priceInputs.id}
            priceInputProps={priceInputs}
            priceInputError={price.error}
            priceInputHasError={price.hasError}
            dateInputId={dateInputs.id}
            dateInputProps={dateInputs}
            dateInputError={date.hasError}
            dateInputHasError={date.error}
            submit={(e) => onSubmit(e)
              .then(res => {
                console.log('done');
              })
            }
            disabled={disabled}
          />
        </Col>
      </Row>
    );
  }
}

Home.propTypes = {};

Home.defaultProps = {};

export default Home;
foxhound87 commented 6 years ago

You should have implemented the onSubmit hook which already handles promises.