0

I have a component MockTablein which I'm fetching an API response and storing into a variable data. I want to use the data in another component UsecasePane. May I know the correct way to pass data to UsecasePane?

  • MockTable is the child component of MainContent.
  • AddMock is the child component of MainContent.

=> MainContent.js

const MainContent = () => {
  return (
    <div >
      <CustomerTable />
      <AddMock />
      <MockTable />
    </div>
  );
};

export default MainContent;

Now TabContent is the child of AddMock

=> AddMock.js

const AddMock = () => {
  return (
    <div className="row">
              <Container className="mockbody">
                <Header as="h3">Hierarchy</Header>
                <TabContent />
              </Container>
    </div>
  );
};

export default AddMock;

UsecasePane is nested inside the TabContent.

// TabContent.js

import React from "react";
import { Tab } from "semantic-ui-react";
import UsecasePane from "./UsecasePane";

const panes = [
  {
    menuItem: "Usecase",
    render: () => <Tab.Pane attached={false}>{<UsecasePane />}</Tab.Pane>,
  }
];

const TabContent = () => (
  <Tab menu={{ secondary: true, pointing: true }} panes={panes} />
);

export default TabContent;

=> MockTable.js

import React, { useState, useEffect } from "react";
import "../assets/css/material-dashboard.css";
import axios from "axios";
import { Icon, Dimmer, Loader, Segment } from "semantic-ui-react";

let config = require("../appConfiguration");

const MockTable = () => {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(false);

  const fetchData = async () => {
    const response = await axios
      .get(config.App_URL.getAllTest, {
        params: {
          customHostName: "suite",
          type: "routes",
        },
      })
      .catch((error) => {
        console.error(`Error in fetching the data ${error}`);
      });
    let list = [response.data];
    setData(list);
    setLoading(true);
  };

  useEffect(() => {
    fetchData();
  }, []);

  
return (
     // rest of the code
)
  
};
export default MockTable;

Now I need to pass this data to UsecasePane which is nested in another component TabContent

Akhil Suseelan
  • 217
  • 1
  • 5
  • 25

3 Answers3

2

There are multiple ways to pass data to child components.

  1. Using props

//In Grandparent
<Parent dataToParent={someData}/>

//In Parent 
<Child dataToChild={props.dataToParent}/>

//Using in Child
render(){
   {props.dataToChild}
}
  1. Render props(skipping a hierarchy)

https://reactjs.org/docs/render-props.html

//In Grandparent component
<Parent dataToParent={dataFromParent => (
  <Child>{dataFromParent}</Child>
)}/>
  1. Storing data in the central state

Context API: https://reactjs.org/docs/context.html

Third-Party Libs:

i.e. Redux https://redux.js.org/

Edit:

Thank you for updating the question. By looking at the hierarchy it seems that you are dealing with sibling components. i.e. MainContent -> MockTable is where you are fetching the data and MainContent -> AddMock -> TabContent -> UsecasePane where you want to display/pass the data. For both of these the immediate common parent is MainContent.

If you don't want to use ContextAPI or any other third-party lib. I would suggest to lift up your states to MainContent and pass a callback function to MockTable and pass state to MainContent -> AddMock -> TabContent -> UsecasePane

Consider the following example:

//MainContent.js
const MainContent = () => {
  ...
  const [data, setData] = useState([]);
  ...
  return (
    <div >
      <CustomerTable />
      <AddMock setData={setData} /> {/*Passing callback function*/}
      <MockTable data={data} /> {/*Passing state*/}
    </div>
  );
};

export default MainContent;
//AddMock.js
...
const fetchData = async () => {
    // all data fetching logic remains the same.
    let list = [response.data];
    props.setData(list); //Calling callback function to set the state defined in MainContent.js
  };

useEffect(() => {
    fetchData();
  }, []);
...
//AddMock.js

const AddMock = ({data}) => {
  return (
    <div className="row">
              <Container className="mockbody">
                <Header as="h3">Hierarchy</Header>
                <TabContent data={data} /> {/*Passing data props*/}
              </Container>
    </div>
  );
};

export default AddMock;

//TabContent.js

const TabContent = ({data}) => {
const panes = [
  {
    menuItem: "Usecase",
    render: () => <Tab.Pane attached={false}>{<UsecasePane data={data} />}</Tab.Pane>,
  }
];
 return(
  <Tab menu={{ secondary: true, pointing: true }} panes={panes} />
 )
}

export default TabContent;
//

Here you can see that to pass data, some prop drilling is happening, though it's not an anti pattern but would recommend to avoid as much as possible spcially in the functional components as they don't have support for shouldComponentUpdate() or React.PureComponent otherwise you can use React.memo HOC Read more.

Of Course React.Context or Third party libs i.e. Redux(Though it'll be overkilled for small applications) are few alternatives to all of these.

Pramod Mali
  • 1,588
  • 1
  • 17
  • 29
  • Thanks Pramod for the detailed explanation. Let me try out this. :) – Akhil Suseelan Aug 05 '20 at 14:03
  • Hey I thought i'll stick with context API now.. so my doubt is if i encapsulate my component as like ``
    `` Will the nested components of input1 can fetch data?
    – Akhil Suseelan Aug 05 '20 at 17:24
  • You need to define how context will look. Create your context using React.Context. Wrap everything in MainContent.js in Context.Provider with an initial value. Your MockTable.js will remain the same i.e. receiving callback function and updating state in the parent. In the UsecasePane component use, 'useContext' hook to get the value from context. For your ref: https://reactjs.org/docs/hooks-reference.html#usecontext and https://stackoverflow.com/a/54738889/6133559 – Pramod Mali Aug 06 '20 at 04:35
1

Edited

Try to fetch your data in MainContent to centralize your datas and then dispatch. Use a callback (addData) to pass the new data to your parent

MainContent

const MainContent = () => {
    // get datas

    function addData(newData){
        setData([...data, newData])
    }

    return (
      <div>
        <CustomerTable />
        <AddMock addData={addData} />
        <MockTable data={data} />
      </div>
    );
};

AddMock

const AddMock = (props) => {
    // use props.addData(newData)
};
BloodyMonkey
  • 1,572
  • 1
  • 9
  • 15
0

You can create a function which will return your array then you can pass an argument to that function which will be consumed in usecasepane component.

const panes = (arg) => {
// do something with arg...
return [
  {
    menuItem: "Usecase",
    render: () => <Tab.Pane attached={false}>{<UsecasePane arg={arg} />}</Tab.Pane>,
  }
];
}

const TabContent = () => (
  <Tab menu={{ secondary: true, pointing: true }} panes={ panes() } />
);
EugenSunic
  • 13,162
  • 13
  • 64
  • 86