React-redux: setState warning using v5.0.1 with redux-form

Created on 27 Dec 2016  路  2Comments  路  Source: reduxjs/react-redux

Similar to the issue #564 .

Just like in the above mentioned issue, I have a hidable form. The difference is: I have a ParentContainer that passes the visible prop to the ConnectedParent. Yet everything is fine unless I hide the Form dispatching asynchronously. In this case I get a warning:

setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the Connect(Form(Form)) component.

I'm sorry, but i can't provide a clonable example. Some code to reproduce this bug:

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

const Form = () => <Field type="text" component="input" name="field" />;
const ReduxForm = reduxForm({ form: 'example' })(Form);


class Parent  extends Component {
  componentWillReceiveProps(newProps) {
    if (this.props.visible !== newProps.visible) {
      if (!newProps.visible) {
        newProps.dispatch({ type: 'ANYTHING' });
      }
    }
  }

  render() {
    return <div>{this.props.visible ? <ReduxForm /> : null}</div>;
  }
}
const ConnectedParent = connect()(Parent);


const ParentContainer = ({ dispatch, visible }) => (
  <div>
    <ConnectedParent visible={visible} />
    <button type="button" onClick={() => { dispatch({ type: 'SHOW_FORM' });} }>show</button>
    <button type="button" onClick={() => { dispatch({ type: 'HIDE_FORM' });} }>hide</button>
    <button type="button" onClick={() => { setTimeout(() => { dispatch({ type: 'HIDE_FORM' }); }, 0)} }>hide with timeout</button>
  </div>
);

const mapStateToProps = state => ({ visible: state.app.form });
const ConnectedParentContainer = connect(mapStateToProps)(ParentContainer);

export default ConnectedParentContainer ;

Corresponding reducer:

const initialState = {
  form: false,
};

export default (state = initialState, action) => {
  switch (action.type) {
    case 'SHOW_FORM':
      return Object.assign({}, state, {
        form: true,
      });
    case 'HIDE_FORM':
      return Object.assign({}, state, {
        form: false,
      });
    default:
      return state;
  }
};

Use the ConnectedParentContainer component and the reducer in your app.

Steps to reproduce:
1) Click "show" button.
2) Click "hide with timeout" button.
3) Experience the Warning.

bug

Most helpful comment

I was able to repro the bug above. I refactored out redux-form and ended up with this:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';
import { createStore } from 'redux';

const store = createStore((state = { visible: false }, action) => {
  switch (action.type) {
    case 'SHOW_FORM': return { visible: true };
    case 'HIDE_FORM': return { visible: false };
    default: return state;
  }
});


const AAA = () => <div>Hello</div>;
const BBB = connect(state => state)(AAA);


class CCC extends Component {
  componentWillReceiveProps(newProps) {
    if (this.props.visible && !newProps.visible) {
      newProps.dispatch({ type: 'ANYTHING' });
    }
  }

  render() {
    return <div>{this.props.visible ? <BBB /> : null}</div>;
  }
}
const DDD = connect()(CCC);


const EEE = props => <DDD {...props} />;
const FFF = connect(state => state)(EEE);


ReactDOM.render(
  <Provider store={store}>
    <FFF />
  </Provider>,
  document.getElementById('root')
);
store.dispatch({ type: 'SHOW_FORM' })
store.dispatch({ type: 'HIDE_FORM' });

It appears #579 (fixed #577) also fixes this one.

All 2 comments

@osigum Can you provide more details? What does you store creation look like? What does your top-level component look like? Are you using the latest versions of react/redux-form/etc? There's not quite enough here to troubleshoot without making assumptions.

I was able to repro the bug above. I refactored out redux-form and ended up with this:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';
import { createStore } from 'redux';

const store = createStore((state = { visible: false }, action) => {
  switch (action.type) {
    case 'SHOW_FORM': return { visible: true };
    case 'HIDE_FORM': return { visible: false };
    default: return state;
  }
});


const AAA = () => <div>Hello</div>;
const BBB = connect(state => state)(AAA);


class CCC extends Component {
  componentWillReceiveProps(newProps) {
    if (this.props.visible && !newProps.visible) {
      newProps.dispatch({ type: 'ANYTHING' });
    }
  }

  render() {
    return <div>{this.props.visible ? <BBB /> : null}</div>;
  }
}
const DDD = connect()(CCC);


const EEE = props => <DDD {...props} />;
const FFF = connect(state => state)(EEE);


ReactDOM.render(
  <Provider store={store}>
    <FFF />
  </Provider>,
  document.getElementById('root')
);
store.dispatch({ type: 'SHOW_FORM' })
store.dispatch({ type: 'HIDE_FORM' });

It appears #579 (fixed #577) also fixes this one.

Was this page helpful?
0 / 5 - 0 ratings