Nurturing child components in React
Understanding React is one of the toughest thing I’ve ever done in my development life. Everything in React looks very simple, feels very simple yet it’s kind of complex. A relevant example can be the title of my post, Nurturing child components in React. I couldn’t find a proper title for a post where I will explain parent-child component communication, importance of container components in routing and a little bit usage of redux.
Parent & Child Component
Parent, Child, Container components all these are just relative names. Sometimes developers follow some conventions in giving names. We can follow that. Now let’s dive into the real thing.
export default class Father extends Component {
render() {
return (
<div>
<Son />
<Daughter />
</div>
);
}
}
If I didn’t explain anything, you would have still understood that Father is the parent component here and Son & Daughter are the child components.
Communication between parent & child components
Sending data to child from parent
This is little bit weird but communication between parent & child component is unidirectional. It means child component will always receive data from their parent but they won’t able to send something by themselves. First let’s look at receiving data from parent:
export default class Father extends Component {
render() {
return (
<div>
<Son command="Bring the airgun" />
<Daughter command="Start the car" />
</div>
);
}
} export default class Son extends Component {
render() {
return (
<div>
Father is saying {this.props.command}
</div>
);
}
} export default class Daughter extends Component {
render() {
return (
<div>
Father is saying {this.props.command}
</div>
);
}
}
Sending data to parent from child
Now what happens when the son or daughter wants to say something to their father? They can’t do anything about it unless the father wants to listen. Parents are very strict in react. Unless they send any callback function, the children have nothing to do expect receiving data from the parent component.
export default class Father extends Component {
tellMe(something) {
console.log(something);
}
render() {
return (
<div>
<Son onSpeak={this.tellMe.bind(this)} command="Bring the airgun" />
<Daughter command="Start the car" />
</div>
);
}
}
export default class Son extends Component {
render() {
return (
<div>
Father is saying {this.props.command}
<input onChange={this.props.onSpeak} />
</div>
);
}
}
It’s a good practice to reduce usage of state in react components. Always a child component should depend on its parent for new data. If we can write such independent child components, we can reuse them in many places. That means we can write less code and boost our development.
Sending data to siblings
What happens if the son component wants to send something to the daughter? Well the son component can’t do it directly. First he needs to send it to his father where the father passes a callback function to get that data. Then the father passes a props to his daughter.
export default class Father extends Component {
constructor() {
super();
this.State = {
message: '';
};
}
tellMe(something) {
console.log(something);
this.setState({
message: something
});
}
render() {
return (
<div>
<Son onSpeak={this.tellMe.bind(this)} />
<Daughter message={this.state.message} />
</div>
);
}
} export default class Son extends Component {
render() {
return (
<div>
<input onChange={this.props.onSpeak} />
</div>
);
}
} export default class Daughter extends Component {
render() {
return (
<div>
Brother is saying {this.props.message}
</div>
);
}
}
Importance of using container components
Container components are just like parent components. Basically all redux communication logic and other logic happen inside container components and finally we render necessary child components. Usually if you need to get some data from API and distribute the data to multiple components, then you might do the API job inside a container component and pass the necessary data as props to the child components.
class UserListCont extends Component {
componentsDidMount() {
this.props.getUsers();
}
render() {
return (
<UserList list={this.props.users} />
);
}
}
function mapStateToProps(state, ownProps) {
return {
users: state.users
};
}
export default connect(mapStateToProps, {
getUsers
})(UserListCont);
You might ask me that I could call getUsers in the UserList component but why I am using the UserListCont component? Well I could do that but what if there was another API which returns different sets of users? In that case I had to create another component similar to UserList component. That’s a mess right? We are using React to create reusable components. But if you use a container component then you just pass the users list as props in the UserList component and baam!
Using container components while routing
I will explain the scenario with a very straightforward example. We should use container components while routing because we can’t write any logic inside the router. Erm, you can but just don’t! Now consider you have a website where you want to show Home component in the “/” route while the user is logged in. Else you want to show the Login component in the “/” route. Now clearly you don’t want write a condition inside the router. So here comes the container component.
export default class App extends Component {
render() {
return (
<Router history={browserHistory}>
<Route path="/" component={HomeCont} />
<Route path="/other" component={OtherCont} />
);
}
}
class HomeCont extends Component {
constructor() {
super();
this.State = {
logged: false;
};
}
componentsDidMount() {
this.props.getUserMe();
}
componentWillReceiveProps(nextProps){
this.setState({
logged: nextProps.userMe
});
}
render() {
return (
{this.state.logged ? <Home /> : <Login />}
);
}
}
function mapStateToProps(state, ownProps) {
return {
userMe: state.userMe
};
}
export default connect(mapStateToProps, {
getUserMe
})(HomeCont);
So you see that the router is neat and clean and all the logics are inside the HomeCont. If more logics are necessary for the OtherCont, then we can write them inside the OtherCont component. Thus we can keep the router clean and show whatever component on a single route conditionally.
I hope this article made some sense about nurturing React child components. I skipped module imports and component imports on top of the class declaration. Pardon my laziness.