1

I'm using React-Redux to build a web app. In a parent component ("Layout"), I separate the page into two columns A and B.

Column A is a react component ("NavMenu") which also contains the title of the B column content. The B column is where the main component is displayed and it has its own title.

What I want to do is send this title from the B component to its sibling A component. What I've learned so far is that I need to send this data to the parent component (Layout) and then from there send it to A through a callback function. The function is a prop I've added to all children in this.props.children in the Layout component.

In the Layout component, the content in B is one of the children component in this.props.children.

Here is my code:

Layout.js

import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { actionCreators } from '../store/Layout';
import { Col, Grid, Row } from 'react-bootstrap';
import NavMenu from './NavMenu'; // Component A

class Layout extends React.Component {

    myCallback = titleFromChild => {
        this.props.changeTitle(titleFromChild);
    }

    render() {
        const childrenWithCallback = React.Children.map(this.props.children, (child) => {
            return React.cloneElement(child, { titleCallback: this.myCallback });
        });
        return (
            this.props.isLoading ?
                <p className='clearfix text-center'><span>Loading...</span></p>
                :
                <Grid fluid>
                    <Row>
                        <Col sm={3}>
                            <NavMenu title={this.props.title} />
                        </Col>
                        <Col sm={9}>
                            {childrenWithCallback}
                            {console.log(childrenWithCallback)}
                            // Console.log here shows that all children in this.props.children have the new prop
                        </Col>
                    </Row>
                </Grid>
        );
    }}
    export default connect(
        state => state.layout,
        dispatch => bindActionCreators(actionCreators, dispatch)
        )(Layout);

I've added the function this.myCallback to this.props.children like in this tutorial.

LayoutStore.js

const changeTitleType = 'CHANGE_TITLE';
const initialState = {
    title: "Child title",
    isLoading: true
};

export const actionCreators = {
    changeTitle: title => ({ type: changeTitleType, title })
}

export const reducer = (state, action) => {
    state = state || initialState;

    if (action.type === changeTitleType) {
        return {
            ...state,
            title: action.title
        };
    }

    return state;
};

I've checked this similar question and this other similar question and I understand how it's supposed to work, but my problem goes a bit further: the child component doesn't receive the new injected prop.

ComponentB.js

import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { actionCreators } from '../store/ExpeditionCommands';

class ComponentB extends React.Component {

    componentWillMount() {
        this.props.titleCallback(this.props.title);
        console.log(this)
        //This is where I am not receiving the prop (titleCallback) I sent from the parent component    
    }

    render() {
        return (
            <div>Hi there</div>
        );
    }
}

export default connect(
    state => state.componentStoreB,
    dispatch => bindActionCreators(actionCreators, dispatch)
)(ComponentB);

Why is my child component not receiving the new prop injected in this.props.children? Do I need to pass through my redux store?

Jimmy Jacques
  • 346
  • 4
  • 14
  • 2
    You should not `send this data to the parent component (Layout) and then from there send it to A through a callback function`. A common technique for when siblings needs to collaborates is to [lift the state up into the first common ancestor](https://reactjs.org/docs/lifting-state-up.html). – Tholle Jul 04 '18 at 13:12
  • 1
    @Tholle At first I thought lifting state was impossible in my situation because this.props.children was rendered through a route but I realised everything would be much simpler if I had a unique redux store for all components and "store" my shared props in there. Everything works fine now. Thank you for the hint. – Jimmy Jacques Jul 05 '18 at 17:22

0 Answers0