Fernando Maymone
Fernando Maymone

Reputation: 355

How to get initialValues for a reduxFrom from state?

I am using my forms with react redux. I have the same form for editing and creating a new one.

I want, when opening my form, that, if editing, I put the initial values of the form, from my api.

I have already tried the official documentation, but its not working.

This is my Form:

class Form extends Component {

  componentDidMount () {
    const tripId = this.props.match.params.uid;
    if(tripId){
      this.fetchData(tripId);
    }
  }

  fetchData = async (tripId) => {
    axios.defaults.headers.common['Authorization'] = 'token';
    axios.defaults.headers.common['Accept'] = 'application/vnd.trips.v1+json';
    await axios.get(`myurl`) 
    .then(res => {
      const trip = res.data;
      this.setState({ 
        trip: trip,
        isLoading: false,
        initialValues: trip
      });
    })
    console.log('Terminou de carregar')
  }

(....)
Form = reduxForm({ form: 'trip', enableReinitialize : true })(Form)
const mapStateToProps = state => {
  const { intl, vehicleTypes, users, dialogs, trip } = state

  return {
    intl,
    vehicleTypes,
    users,
    dialogs,
    initialValues: trip
  }
}

export default connect(
  mapStateToProps, { setDialogIsOpen }
)(injectIntl(withRouter(withTheme()(Form))))

But my initialValues are never filled, and comes always blank. I debuged the code, and I see that my API is loading and setting the state on my fetchData method. So, what Im doing wrong here?

Upvotes: 0

Views: 403

Answers (2)

aquilesb
aquilesb

Reputation: 2262

As @d7my said, your mistake is that you are trying to change a prop through the setState, they are different things in react world. The fastest way to fix your problem, it would be replacing that:

this.setState({
   trip: trip,
   isLoading: false,
   initialValues: trip
});

For:

this.props.initialize(trip)

The prop initialize is exported by redux-form to your component to update the form initial state. I made a codesandbox where you can check it working.

Said that, I believe you should change the structure of your component, instead of loading your data inside the component (componentDidMount), you should do it inside a redux action and then update your state. Doing this way, you could easily get the form data from your redux state on mapStateToProps function and set initialValues prop instead of use initialize props, that is much better.

Upvotes: 0

d7my
d7my

Reputation: 197

First, you're trying to pass initialValues through this.setState which is not going to work, reduxForm expect initialValues to be passed as props.

One thing to mention, there is a difference between the application state and component local state, first one -application state- is clearly managed by redux, and the other one -local component state- is managed by react which you can modify by calling this.setState

so to fix this, you should be dispatching an action when you receive the data from your api, and you update your props in mapStateToProps

your code should look like:

class Form extends Component {

  componentDidMount () {
    const tripId = this.props.match.params.uid;
    if(tripId){
      this.fetchData(tripId);
    }
  }

  fetchData = async (tripId) => {
    await axios.get(`https://toptal-backend-fmaymone.c9users.io/trips/${tripId}`) 
    .then(res => {
      const trip = res.data;
      this.props.fillTheForm(trip);
    })
    console.log('Terminou de carregar')
  }

(....)
Form = reduxForm({ form: 'trip', enableReinitialize : true })(Form)
const mapStateToProps = state => {
  const { intl, vehicleTypes, users, dialogs, trip } = state

  return {
    intl,
    vehicleTypes,
    users,
    dialogs,
    initialValues: trip
  }
}

const fillTheForm = (dispatch) => (trip) => {
  // here you dispatch the action and update your application state
  // in your reducer when this action is dispatched,
  // then mapStateToProps should be called and the data you just
  // passed in your reducer should be in (state)
  dispatch()
}

export default connect(
  mapStateToProps, { setDialogIsOpen, fillTheForm }
)(injectIntl(withRouter(withTheme()(Form))))

Upvotes: 1

Related Questions