Search code examples
javascriptreactjsredux-formreact-router-v4react-router-redux

Redux-form empties itself when clicking on a Link component which loads the same form


I have a profile page containing a redux-form ProfileForm for which I set some initialValues. In my page header, I have a react-router Link to the /profile route.

The first time the page loads, the form is initialized correctly. However, if I click on the Link element, the form empties itself. I would have expected the form to keep its values maintained by the redux-form state (or at least to be initialized to initialValues).

What am I doing wrong? Is there a workaround?

Note: I am using react 16, react-router 4 and redux-form 7. I am also using redux-thunk in my action generators when fetching data.

Code

Profile.js

The component Profile waits for the initialValues to be set before rendering the ProfileForm for the first time. It shows a Loading component until the data is fetched.

//...
import { 
    fetchData,
    submitData,
} from '../../actions';

class Profile extends Component{
    componentDidMount() {
        this.props.fetchData();
    }

    render(){
        if(!this.props.initialValues){
            return <Loading />
        }else{
            return <ProfileForm initialValues={this.props.initialValues} />
        }
    }
}


class ProfileForm extends Component{
    onSubmit(values){
        return this.props.submitData(values);
    }

    render(){
        const { handleSubmit } = this.props;
        return (
            <div>
                <Form 
                    onSubmit={handleSubmit(this.onSubmit.bind(this))}
                    className="container">
                    <Field
                        name="first_name"
                        type="text" 
                        title="First name" 
                        component={SingleInput} />

                    ...

                    <Button type="submit" 
                        Sumbit
                    </Button>
                </Form>
            </div>
        )
    }
}

// validate, warn, etc.
// ...

function mapStateToProps(state) {
    return { 
        initialValues: state.profile.data // set by the profile reducer upon fetching the data
    };
}

export default connect(mapStateToProps,{ fetchData })(Profile);

ProfileForm = reduxForm({
    form: 'ProfileForm',
    fields: ['first_name', ...],
    enableReinitialize: true,
    validate,
    warn,
})(
    connect(mapStateToProps, { submitData })(ProfileForm)
);

App.js

//src/components/App.js
render() {
    return (
        <div className="App">
            <Header />
            <Main />
        </div>
    );
}

Header.js

The Header component contains, among other things, a Link component pointing to /profile.

//src/components/header.js
render(){
    return (
        ...
        <Link className="nav-link" to="/profile">Profile</Link>
        ...
    )
}

Main.js

I have a profile page that is accessible thanks to React-Router v4 under /profile

//src/components/main.js
render(){
    return (
        <Switch>
            <Route path='/profile' component={Profile}/>
            ...
        </Switch>
    )
}

EDIT: actions generator and reducers

I am using axios to fetch and submit data and redux-thunk to dispatch callbacks once I receive the data.

Action generators

//src/actions/index.js
export function fetchData(){
    return (dispatch) => {
        axios.get(`${FETCH_URL}`)
            .then(response => {
                dispatch({
                    type: FETCH_DATA_SUCCESS,
                    payload: response.data
                });
            })
            .catch(error => {
                dispatch({
                    type: FETCH_DATA_FAILED,
                    payload: error
                })
            })
    }
}

export function submitData(values){
    return (dispatch) => {
        return axios.post(`${SUBMIT_URL}`,values)
                    .then(response => {
                        dispatch({ 
                            type: SUBMIT_DATA_SUCCESS,
                            payload: values,
                        });
                    })
                    .catch(error => {
                        dispatch({
                            type: SUBMIT_DATA_FAILED,
                            payload: error
                        });
                    })
    };
}

Reducers

//src/reducers/profile.js
export default function(state={}, action) {
    switch(action.type) {
        case FETCH_DATA_SUCCESS:
            return { ...state, profile: action.payload };
        case FETCH_DATA_FAILED:
            // Note that I never reach this
            return { ...state, profile: {} };
        case SUBMIT_DATA_SUCCESS:
            return { ...state, profile: action.payload };
        case SUBMIT_DATA_FAILED:
            return { ...state };
    }
    return state;
}

Solution

  • Your problem is that the form is unmounted/mounted for whatever reason, I glanced your code and my first thought is the Loading component. When it renders, the Form is not. What you can do is on the following: Hide the form instead of removing it (pass a isVisible or something to the form) or let redux-form keep the state for you when the form is unmounted. You can do this by setting the prop destroyOnUnmount=false to the reduxForm hoc.