Pedro Vinícius
Pedro Vinícius

Reputation: 496

Component is not receiving current, but previous input values

I have two components to represent a list of articles and a filtering form. Every time any form field is changed, I need to send a HTTP request including the selected filters.

I have the following code for the SearchForm:

import React from 'react';
import { reduxForm, Field } from 'redux-form';

const SearchForm = ({ onFormChange }) => (
  <form>
    <Field component='select' name='status' onChange={onFormChange}>
      <option>All</option>
      <option value='published'>Published</option>
      <option value='draft'>Draft</option>
    </Field>

    <Field
      component='input'
      type='text'
      placeholder='Containing'
      onChange={onFormChange}
    />
  </form>
);

export default reduxForm({ form: 'myCustomForm' })(SearchForm);

And the following for the PostsList:

import React, { Component } from 'react';
import SearchForm from './SearchForm';
import { dispatch } from 'redux'; 

class PostsList extends Component {
  constructor(props) {
    super();

    this.onFormChange = this.onFormChange.bind(this);
  }

  onFormChange() {
    // Here I need to make the HTTP Call.
    console.info(this.props.myCustomForm.values);
  }

  componentWillMount() {
    this.props.actions.fetchArticles();
  }

  render() {
    return (
      <div>
        <SearchForm onFormChange={this.onFormChange} />            

        <ul>
          { this.props.articles.map((article) => (<li>{article.title}</li>)) }
        </ul>
      </div>
    );
  }
}

const mapStateToProps = (state) => ({
  myCustomForm: state.form.myCustomForm
});

const mapDispatchToProps = (dispatch) => ({
  actions: {
    fetchArticles: dispatch({ type: 'FETCH_ARTICLES' })
  }
});

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

Though there is nothing going wrong with the rendering itself, something very awkful is happending with the myCustomForm.values prop when I change the form.

When I do that for the first time, the console.log(this.props.myCustomForm.values) call returns undefined, and the next calls return the previous value.

For example:

  1. I load the page and select the draft option. undefined is printed.
  2. I select published. { status: 'draft' } is printed.
  3. I select draft again... { status: 'published' } is printed.

I inspected the redux store and the componend props. Both change according to the form interaction. But my function is returning the previous, not the new value sent by onChange.

This is clearly a problem with my code, most probably with the way I'm passing the function from parent to child component.

What am I doing wrong?

Upvotes: 1

Views: 95

Answers (1)

Tomasz Mularczyk
Tomasz Mularczyk

Reputation: 36179

There is nothing wrong with your function. What I think is happening is that first time you select the option your callback is fired and is console logging current state for myCustomForm.values which haven't been yet changed by redux-form. So when the select changes:

  1. your callback is fired...
  2. ...then redux-form is updating the state.

So. when your callback is making console.log it's printing not yet updated store.

do this, and you will see it's true:

  onFormChange(e) {
    // Here I need to make the HTTP Call.
    console.info(e.currentTarget.value);
  }

EDIT

My first question would be, do you really need to store this value in redux and use redux-form? It's a simple case, and you get current value in a way I showed you above.

However, if that's not the case, the callback is not required here, you just need to detect in your connected component (PostsList) that values have been changed in a form. You can achieve it with componentWillReceiveProps hook.

class PostsList extends Component {
  constructor(props) {
    super(props); // you should pass props to parent constructor
    this.onFormChange = this.onFormChange.bind(this);
  }

  componentWillReceiveProps(nextProps) {
     if(this.props.myCustomForm.values !== nextProps.myCustomForm.values) {
         // do your ajax here....
     }
  }

  componentWillMount(nextProps) {
    this.props.actions.fetchArticles();
  }

  render() {
    return (
      <div>
        <SearchForm />            

        <ul>
          { this.props.articles.map((article) => (<li>{article.title}</li>)) }
        </ul>
      </div>
    );
  }
}

Upvotes: 1

Related Questions