Search code examples
reactjsreact-reduxreact-boilerplate

How to pass componentDidMount function to deep layer children in React


I have question about passing componentDidMount function from parent to deep laying child. I have a list of items, which are selected by items status. After I change one of the item's status, I need to re-render parent to get new data. Tricky part for me is that, that I can't find way, how to pass componentDidMount function or action to fetch my list's data again.

My parent class:

class Page extends React.Component {
  componentDidMount() {
    this.props.onCompMount();
  }

  render() {
    const { error, loading, list } = this.props;

    const pageListProps = {
      loading,
      error,
      list,
    };

    return (
      <article>
        <div>
          <PageList {...pageListProps} />
        </div>
      </article>
    );
  }
}

My 1st child:

function PageList({ loading, error, list }) {
  if (loading) {
    return <List component={LoadingIndicator} />;
  }

  if (error !== false) {
    const ErrorComponent = () => (
      <ListItem item="Something went wrong, please try again!" />
    );
    return <List component={ErrorComponent} />;
  }

  if (list !== false) {
    return <List items={list} component={PageItem} />;
  }
  return null;
}

2nd child:

export class PageItem extends React.PureComponent {
  constructor() {
    super();

    this.state = {
      modalIsOpen: false,
    };

    this.openModal = this.openModal.bind(this);
    this.closeModal = this.closeModal.bind(this);
  }

  openModal() {
    this.setState({ modalIsOpen: true });
  }

  closeModal() {
    this.setState({ modalIsOpen: false });
  }

  render() {
    const { item } = this.props;

    // Put together the content of the repository
    const content = (
      <Wrapper>
        <h3>{item.title}</h3>
        <button onClick={this.openModal}>Decline</button>
        <Modal
          isOpen={this.state.modalIsOpen}
          onRequestClose={this.closeModal}
          style={customStyles}
          contentLabel="Preview"
        >
          <Form close={this.closeModal} />
        </Modal>
      </Wrapper>
    );

And my last child where I want after submit to re-render parent container:

export class Form extends React.Component {

  render() {
    return (
      <article>
        <form
          onSubmit={e => {
            e.preventDefault();
            this.props.submit();
            this.props.close();
            //Somehow re-render parent
          }}
        >
          <div className="row" style={{ textAlign: 'start' }}>
            Do you really want to change status?
            <div className="col-md-12 buttonContainer">
              <ButtonA
                label="Submit"
                style={{ width: '50%' }}
                primary
                type="submit"
              />
            </div>
          </div>
        </form>
      </article>
    );
  }
}

What I have tried is to reload page with window.location.reload(); and it works. But I think it is bad practice with React. Maybe someone could advise me how to make it better?

EDIT: I am adding parent reducer and 4th child reducer.

Parent reducer:

const initialState = fromJS({
  loading: false,
  error: false,
  listData: {
    list: false,
  },
});

function pageReducer(state = initialState, action) {
  switch (action.type) {
    case FETCH_LIST_BEGIN:
      return state
        .set('loading', true)
        .set('error', false)
        .setIn(['listData', 'list'], false);
    case FETCH_LIST_SUCCESS:
      return state
        .setIn(['listData', 'list'], action.list)
        .set('loading', false);
    case FETCH_LIST_FAILURE:
      return state.set('error', action.error).set('loading', false);
    default:
      return state;
  }
}

export default pageReducer;

4th child reducer:

const initialState = fromJS({});

function formReducer(state = initialState, action) {
  switch (action.type) {
    case SUBMIT:
      return state;
    default:
      return state;
  }
}

export default formReducer;

Solution

  • We use Redux or React's new Context API to avoid prop drilling issue in react. In your use you can dispatch action from parent component and connect relevant reducer to your 4th level child. So when your reducer updates global state (store), your connected component will re-render and take updated state as in props.