Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InitialValues from state with state load during the rendering #1473

Closed
pieval opened this issue Aug 5, 2016 · 7 comments
Closed

InitialValues from state with state load during the rendering #1473

pieval opened this issue Aug 5, 2016 · 7 comments

Comments

@pieval
Copy link

pieval commented Aug 5, 2016

I have some trouble trying to initialize a redux-form decorated form with some data fetched from a backend (async with redux-thunk) and stored in redux state.
The fact is I need to perform the fetch when a user display the page where the form is and populate the initialValues of the form with the new state values.

My redux-form is connected to the state like it is explained in your example, and I fetch data with an action triggered on the componentWillMount() method.

But during the first render, the redux-form/INITIALIZE action occurs, setting the initialValues with empty data and my action fetching real data and storing them into the state come after. But there is no refresh on the displayed form.

Do you have a specific pattern to work with this case ?
How does it works in your exemple to be able to reload initialValues (and pristine state) when clicking on Load Account button ?

PS : I'm using redux-form 6.0.0.rc4

@rkoberg
Copy link

rkoberg commented Aug 5, 2016

Just guessing, but I needed to set enableReinitialize to true:

RecordForm = reduxForm({
  form: 'recordForm',
  enableReinitialize: true,
})(RecordForm);

Another option would be to use https://github.com/makeomatic/redux-connect instead of implementing componentWillMount. I do this on a wrapper "page" component - see #1407 (comment)

@sir4ur0n
Copy link

sir4ur0n commented Aug 5, 2016

Why not just preventing rendering until you received the backend data?

/* MyFormContainer.js */
// render method
render() {
        if (this.props.loadInProgress === true) { // <-- Use a state variable to know when the load is done or not
            return false; // <-- Prevent rendering
        } else {
            return (
                <MyForm onSubmit={this.props.myFormHandler} initialValues={this.props.initialData}/> // <-- MyForm is the redux-form
            );
        }
    }

function mapStateToProps(state) {
    return {
        loadInProgress: state.categoryRulesForm.loadInProgress, // <-- Pass the variable from state to props
        // Other mapping 
    }
}

componentWillMount() {
        // Asynchronously load the initial data
        this.props.loadInitialData(this.props.loadParameter);
    }

/* MyFormAction.js */
// loadRequested and loadReceived are classic actions
export function loadInitialData(parameter) {
    // Async function (thunk)
    return function(dispatch) {
        dispatch(loadRequested(parameter));
        return fetch(`http://example/${parameter}`, {
                method: 'GET',
                headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'
                },
            })
            .then(response => response.json())
            .then(json => dispatch(loadReceived(json)));
    }
}

/* MyFormReducer.js */
const initialState = {
    loadInProgress: true
}

export default function loadInitialData(state = initialState, action) {
    switch (action.type) {
        case ActionTypes.LOAD_REQUESTED:
            return assign({}, state, {
                loadInProgress: true
            });

        case ActionTypes.LOAD_RECEIVED:
            return assign({}, state, {
                loadInProgress: false
            });

        default:
            return state;
    }
}

@pieval
Copy link
Author

pieval commented Aug 29, 2016

Thank you,

I'm going to try your solutions !

@sir4ur0n : But in your example does the form will not be initialized during the redux-form decoration in my main container ? Trying your solution there already a first initialization in my action stack before the blocking rendering.

@pieval pieval closed this as completed Aug 29, 2016
@slavab89
Copy link

Sorry that i'm bumping this issue but i wanted to further ask about this.
I've implemented the same flow that was suggested here by @sir4ur0n - using loadInitialData as an async action that fetches the data.
What i'm experiencing is the following:

  1. Component starts to create itself and calls componentWillMount.
  2. An action is dispatched that sets the loadInProgress to true
  3. render function is called with the "old" store still (?!)
  4. redux-form is initialized with an empty object
  5. another render is called because of the INITIALIZE action. This time loadInProgress is true
  6. redux-form is destroyed with DESTROY action
  7. API call returns with data, setting loadInProgress back to false
  8. redux-form is INITIALIZED once again but with the fetched data

I dont think this is the best flow and it has to be a mistake here... anyone cares to pitch in?

@tblazz
Copy link

tblazz commented May 24, 2017

Hello, I'm encountering the same issue. Even with the loadInProgress. In fact what Happened is that :
First time we render the form so : FETCH VERSIONS (versions are the element of the select options) > FETCH_VERSIONS_SUCCESS then redux-form/INITIALIZE then REGISTER FIELD.

After we click cancel for example so RouterDidChange and redux-form-DESTROY.

Then we go again to the form, so FETCH_VERSIONS, redux-form/INITIALIZE, and redux-form/DESTROY is called before FETCH_VERSIONS_SUCCESS.

I tried with enableReinitialize with no success also.

But after the destroy the form is not re-created

@slavab89
Copy link

I've done quite a bit of reading about this.
This is actually not a redux-form issue but rather a more react + redux combined.
You can read the excellent comment that explains the situation and offers a solution (There are a few more in the next comments).

What i did on my end was the same as offered in the comment.
I'm using the following route to display a form to edit some object /:objectId

export default connect((state, ownProps) => {
  const wantedObjectId = ownProps.params.objectId;
  const actualObjectId = state.objectToEdit.id;

  return {
    isFetching: state.isFetching || wantedObjectId !== actualObjectId
   };
})(MyComponent);

So if the isFetching is not yet set because the action did not happen yet, i check what am i supposed to edit against the route.

@lock
Copy link

lock bot commented Jun 1, 2018

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Jun 1, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants