1

I have been trying to add state-management to my react app using React ContextAPI, but I am running into an issue. I am using class components, the idea is here to get data from a component using Axios and pass data to a dashboard to summarize the data.

Component A -- > Provider component

Component B --> Consumer component

Component C -- > Dashboard -- > Consuming ComponentB

When I browse the dashboard, the state value always comes undefined. Any suggestions on how I can resolve this? Appreciate your help.

package.json "react": "^16.12.0", "react-dom": "^16.12.0", "react-router-dom": "^5.1.2", "react-scripts": "3.3.0",

App.js

import React, { Component } from "react";
import "./App.css";
import { Route, Switch } from "react-router-dom";
import Dashboard from "./components/dashboard";
import ComponentA from "./components/a"
import "rsuite/dist/styles/rsuite-default.min.css";
import SomeContext from "./context/context.js";

class App extends Component {
state = {};
render() {
 return (
   <React.Fragment>
     <Sidebar />
       <Switch>
         <Route path="/dashboard" component={Dashboard} />
         <Route path="/a" component={ComponentA} />
       </Switch>
   </React.Fragment>
 );
}
}

export default App;

SomeContext.js


    import React from "react";

    const SomeContext = React.createContext();
    SomeContext.displayName = "SomeContext";


    export default SomeContext;

Component A --> Provider Component

import React, { Component } from "react";
import axios from "axios";
import { Card, CardHeader, Table, Container, Row } from "reactstrap";
import SomeContext from "./context/context.js";
class ComponentA extends Component {
  state = {
    posts: [],
  };
  async componentDidMount() {
    const { data: posts } = await axios.get("http://localhost:5000/api");
    this.setState({ posts });
    console.log(posts);
  }

  render() {
    return (
      <div className="main-content" ref="main-content">

        <Container fluid>
          <SomeContext.Provider value={{ state:this.state }}>
            <ComponentB/>   
            **.....Some component code **

            </SomeContext.Provider>
        </Container>
      </div>
    );
  }
}

export default ComponentA;

Component B -- Consuming data from ComponentA

import React, { Component } from "react";
import SomeContext from "./context/context.js";

class ComponentB extends Component {
static contextType = SomeContext;

  render() {
    return (
      <SomeContext.Consumer>
        {(dashboard) => (
          <div>Data: {dashboard} {console.log(dashboard)} </div>

        )}
    </SomeContext.Consumer>
        );
      }
    }

export default ComponentB;

Dashboard

import React, { Component } from "react";
import ComponentB from "./components/b";

class Dashboard extends Component {
  render() {
    return (
      <div className="main-content">
        <ComponentB />
      </div>
    );
  }
}

export default Dashboard;
18
  • it doesn't look like your Dashboard component is a child of your Provider Commented Apr 5, 2020 at 16:22
  • 1
    You must place your Consumer under (inside) your Provider in order to get it work. ComponentB, which is the Consumer, is not under the Provider, which is ComponentA. Commented Apr 5, 2020 at 16:23
  • @CamSong thanks for your reply, I added ComponentB inside the provider ComponentA. But I still get undefined Commented Apr 5, 2020 at 16:41
  • The ComponentB which is under DashBoard is meaningless, isn't it? You still get what undefined? Commented Apr 5, 2020 at 17:15
  • 1
    Ok, let me try it out, want to set the application the right way so it can grow in the future. thanks again @CamSong. Commented Apr 5, 2020 at 18:46

1 Answer 1

1
  1. In order to get the Consumer work, you must place it under the Provider. As DashBoard is not the Provider but ComponentA, so you can't use the Consumer, which is ComponentB, under DashBoard like that.
  2. For better components organization, I recommend this approach:

DashBoard will be the parent layer (container), which is responsible for 2 things: data fetching and context provider.

ComponentA is now under DashBoard, which still remains the same role: displaying the text data. Besides, it also becomes a Consumer to retrieve data from Provider, which is DashBoard now.

Lastly, ComponentB is nested under DashBoard as well as ComponentA, which is responsible for displaying the graphs.

Please also consider using a meaningful name to name after your components. For example:

ComponentA -> DisplayTextData

ComponentB -> DisplayGraph

So in the end, you will have something like this:

Smart component: Dashboard

Dumb components: DisplayTextData, DisplayGraph

In case you have no idea what smart/dumb component means, please refer to this article written by Dan Abramov: here. Keep in mind that it's always a good practice that you should decrease the number of smart components, and increase the number of dumb components.

  1. In order to keep your components stay clean, please consider switching to Redux. One of the many advantages in using Redux for state management, is that it allows you to avoid calling async actions a.k.a side-effect like data fetching inside your components.
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.