Author avatar

Gaurav Singhal

How to Connect Redux to a Grandchild Component

Gaurav Singhal

  • Nov 25, 2019
  • 9 Min read
  • 13,770 Views
  • Nov 25, 2019
  • 9 Min read
  • 13,770 Views
Web Development
React

Introduction

Hola amigos! In this guide, we'll learn to use Redux—the popular state management library to React—to pass data from a parent component to a grandchild component.

When building web apps, you must have come across a situation where you have to pass data as props from component to component, and a few minutes later you see yourself giving props to a 99th successor component (a bit of an exaggeration, but you get the point). Well, Redux was built precisely for scenarios like these.

Wrapping Root Component with <Provider />

The first step is to wrap the root component of the application with Redux's <Provider /> component.

The <Provider /> component requires the store object. The store object holds the entire state of the application.

1// ..
2import { Provider } from "react-redux";
3
4const App = () => {
5  return (
6    <div className="App">
7      <Provider store={store}>// ..</Provider>
8    </div>
9  );
10};
11
12// ...
jsx

Now the question is, how do we create this store?

Answer: createStore() .

The createStore() function accepts a reducer function and returns a Redux store object that contains the complete state tree of the application.

A reducer is a function that returns the current state of the application.

1// ..
2import { Provider } from "react-redux";
3import { createStore } from "redux";
4
5function reducer() {
6  // ..
7  return {
8    count: 0
9  };
10}
11
12const store = createStore(reducer);
13
14const App = () => {
15  return (
16    <div className="App">
17      <Provider store={store}>// ..</Provider>
18    </div>
19  );
20};
21
22// ...
jsx

Right now, the reducer is only returning the initial state of the application.

index.js

1import React from "react";
2import ReactDOM from "react-dom";
3import { Provider } from "react-redux";
4import { createStore } from "redux";
5
6import "./styles.css";
7
8import Parent from "./Parent";
9
10const initialState = {
11  count: 0
12};
13
14function reducer(state = initialState) {
15  // ..
16  return {
17    count: state.count
18  };
19}
20
21const store = createStore(reducer);
22
23const App = () => {
24  return (
25    <div className="App">
26      <Provider store={store}>
27        <Parent />
28      </Provider>
29    </div>
30  );
31};
32
33// ...
jsx

In a later section of this guide, we'll see how to instruct the reducer to return the current or the changed state.

Building our Components

For this example, we will have 3 components: <Parent />, <Child /> and <GrandChild />. We will create a button in the <Parent /> component and increment the counter value on button click, which will be passed to the <GrandChild /> component.

So, let's begin with the <GrandChild /> component.

GrandChild.js

1import React from "react";
2import { connect } from "react-redux";
3
4const GrandChild = props => (
5  <div className="grandchild-component">
6    <div>This is the grandchild component</div>
7    <div class="emp">Count: {props.count}</div>
8  </div>
9);
10
11const mapStateToProps = state => ({
12  count: state.count
13});
14
15export default connect(mapStateToProps)(GrandChild);
jsx

What's this weird looking connect function that we see above?

connect() is a higher-order function that connects the component with the Redux store. The store is where the global state of the whole application lives.

The connect() function returns a function when we call it, and to the returned function we pass the React component, which then returns a connected component; that's why the connect function call looks so weird.

We then export the connected <GrandChild /> component with all the necessary props.

The mapStateToProps() function is a custom function and not something specific to Redux. Its purpose, as the name of the function suggests, is to return only the necessary data from the global state as props to the component.

Child.js

The <Child /> is going to be a simple component that is only going to wrap the <GrandChild /> component.

1const Child = () => (
2  <div className="child-component">
3    <div>This is the child component</div>
4    <Grandchild />
5  </div>
6);
jsx

Parent.js

We are going to connect the <Parent /> component with Redux, not for accessing the store but for dispatching actions.

An action is simply an object that contains a type property whose value must be a string, and it signifies the purpose of the action. It can contain other optional properties like the payload or data associated with that action.

We will dispatch the INCREMENT action on button click.

1// ...
2import { connect } from "react-redux";
3
4class Parent extends Component {
5  increment = () => {
6    // dispatch the action
7  };
8  render() {
9    return (
10      <div className="parent-component">
11        <div>This is the parent component</div>
12        <button onClick={this.increment}>Click Me!!</button>
13        <Child />
14      </div>
15    );
16  }
17}
18
19export default connect(null)(Parent);
jsx

Since this component does not need the state, we are passing null to the connect() function.

Along with the state, the connected components has access to the dispatch() function, which takes the action object as an argument.

1increment = () => {
2  this.props.dispatch({ type: "INCREMENT" });
3};
jsx

Finishing Up the Reducer

We can pass action as the second parameter to the reducer, and based on the action type, we can return the state from the function.

1const initialState = {
2  count: 0
3};
4
5function reducer(state = initialState, action) {
6  switch (action.type) {
7    case "INCREMENT":
8      return { count: state.count + 1 };
9    default:
10      return {
11        count: state.count
12      };
13  }
14}
jsx

Complete Source Code

index.js

1import React from "react";
2import ReactDOM from "react-dom";
3import { Provider } from "react-redux";
4import { createStore } from "redux";
5
6import "./styles.css";
7
8import Parent from "./Parent";
9
10const initialState = {
11  count: 0
12};
13
14function reducer(state = initialState, action) {
15  switch (action.type) {
16    case "INCREMENT":
17      return { count: state.count + 1 };
18    default:
19      return {
20        count: state.count
21      };
22  }
23}
24
25const store = createStore(reducer);
26
27const App = () => {
28  return (
29    <div className="App">
30      <Provider store={store}>
31        <Parent />
32      </Provider>
33    </div>
34  );
35};
36
37const rootElement = document.getElementById("root");
38ReactDOM.render(<App />, rootElement);
jsx

Parent.js

1import React, { Component } from "react";
2import { connect } from "react-redux";
3
4import Child from "./Child";
5
6class Parent extends Component {
7  increment = () => {
8    this.props.dispatch({ type: "INCREMENT" });
9  };
10  render() {
11    return (
12      <div className="parent-component">
13        <div>This is the parent component</div>
14        <button onClick={this.increment}>Click Me!!</button>
15        <Child />
16      </div>
17    );
18  }
19}
20
21export default connect(null)(Parent);
jsx

Child.js

1import React from "react";
2import Grandchild from "./GrandChild";
3
4const Child = () => (
5  <div className="child-component">
6    <div>This is the child component</div>
7    <Grandchild />
8  </div>
9);
10
11export default Child;
jsx

GrandChild.js

1import React from "react";
2import { connect } from "react-redux";
3
4const GrandChild = props => (
5  <div className="grandchild-component">
6    <div>This is the grandchild component</div>
7    <div class="emp">Count: {props.count}</div>
8  </div>
9);
10
11const mapStateToProps = state => ({
12  count: state.count
13});
14
15export default connect(mapStateToProps)(GrandChild);
jsx

style.css

1.App {
2  font-family: sans-serif;
3  text-align: center;
4}
5
6.parent-component {
7  padding: 15px;
8  border: 1px solid #fc3;
9}
10
11.parent-component button {
12  margin-top: 10px;
13  background: #fc3;
14  border: 0;
15  padding: 10px 18px;
16  color: #000;
17}
18
19.child-component {
20  padding: 15px;
21  border: 1px solid #007bff;
22  margin-top: 10px;
23}
24
25.grandchild-component {
26  padding: 15px;
27  border: 1px solid #f39;
28  margin-top: 10px;
29}
30
31.emp {
32  font-weight: 600;
33  margin-top: 15px;
34  color: #f39;
35}
css

Conclusion

The purpose of this guide was to familiarize you with Redux and the basics of state management in React applications. Complex applications won't have just one reducer or action, so the fundamentals of Redux are essential. I hope you liked this guide. Don't forget to check out my other guides on React.