React-redux: mapStateToProps not firing in nested recursive nodes

Created on 15 Sep 2017  路  4Comments  路  Source: reduxjs/react-redux

I am recursively using a container component which is connected to the store:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { getCategory } from '../../modules/category-list';

class CategoryNodeContainer extends Component {
  componentDidMount() {
    console.log('componentDidMount', this.props);
  }
  render() {
    const { category = {} } = this.props;
    console.log('rendering', category);
    let children = category.children || [];
    return (
      <div className="category-node">
        <span>arrow</span>
        <span>{category.name}</span>
        {children.length > 0 && children.map((child, i) => {
          console.log(' in child map',child);
          return (<CategoryNodeContainer key={i} name={child} />);
        })}
      </div>
    );
  }
}

CategoryNodeContainer.propTypes = {
  category: PropTypes.object,
};

const mapStateToProps = (state, { name }) => {
 console.log(' in maptostate', name)
  return {
    category: getCategory(state, name),
  };
};

const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch);

export default connect(mapStateToProps, mapDispatchToProps)(
  CategoryNodeContainer
);


Here is the data I have:

image

The data is all correct and it actually creates the nested component, but it does not invoke mapDispatchToProps.

In console I see:

index.js:33  in maptostate ALL
index.js:13 rendering {segments: Array(1), text: "All", value: "ALL", name: "ALL"}
index.js:33  in maptostate JACKET
index.js:13 rendering {segments: Array(1), text: "Jackets", label: "CATEGORY", value: "JACKET", name: "JACKET",聽鈥
index.js:20  in child map PARKA
index.js:13 rendering {}
index.js:9 componentDidMount {name: "ALL", category: {鈥}
index.js:9 componentDidMount {name: "PARKA"}
index.js:9 componentDidMount {name: "JACKET", category: {鈥}

So I know the component is getting created and loading, but for some reason not running mapStateToProps which has a selector to grab the container's props from the global state.

I can dispatch an action on interval and only the ALL and JACKET's mapStateToProps methods will invoke, so it doesn't seem to be a state differential issue.

Most helpful comment

Made some changes and looks like it's working when I refer to the connected container:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { getCategory } from '../../modules/category-list';

class _CategoryNodeContainer extends Component {
  componentDidMount() {
  }
  render() {
    const { category = {} } = this.props;
    let children = category.children || [];
    return (
      <div className="category-node">
        <span>arrow</span>
        <span>{category.name}</span>
        {children.length > 0 && children.map((child, i) => {
          return (<CategoryNodeContainer key={i} name={child} />);
        })}
      </div>
    );
  }
}

_CategoryNodeContainer.propTypes = {
  category: PropTypes.object,
};

const mapStateToProps = (state, { name }) => {
  return {
    category: getCategory(state, name),
  };
};

const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch);

const CategoryNodeContainer = connect(mapStateToProps, mapDispatchToProps)(
  _CategoryNodeContainer
);

export default CategoryNodeContainer;

This doesn't seem that clean, is there a better way of doing this?

All 4 comments

Err, it may be that with the nested containers, when referring to <CategoryNodeContainer />, it's referring to the class, and not the connected object.

Made some changes and looks like it's working when I refer to the connected container:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { getCategory } from '../../modules/category-list';

class _CategoryNodeContainer extends Component {
  componentDidMount() {
  }
  render() {
    const { category = {} } = this.props;
    let children = category.children || [];
    return (
      <div className="category-node">
        <span>arrow</span>
        <span>{category.name}</span>
        {children.length > 0 && children.map((child, i) => {
          return (<CategoryNodeContainer key={i} name={child} />);
        })}
      </div>
    );
  }
}

_CategoryNodeContainer.propTypes = {
  category: PropTypes.object,
};

const mapStateToProps = (state, { name }) => {
  return {
    category: getCategory(state, name),
  };
};

const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch);

const CategoryNodeContainer = connect(mapStateToProps, mapDispatchToProps)(
  _CategoryNodeContainer
);

export default CategoryNodeContainer;

This doesn't seem that clean, is there a better way of doing this?

This is a bug tracker, not a support system. For usage questions, please use Stack Overflow or Reactiflux. Thanks!

I admit this should have been on SO, but damn if it didn't solve my problem.

It's probably best practice to never assign a non-connected component to a variable, always wait till it's connected. Otherwise you may grab only half a component.

Was this page helpful?
0 / 5 - 0 ratings