React Functional Component Inheritance

user2052618 picture user2052618 · May 31, 2017 · Viewed 7.1k times · Source

i created a React functional component that is a wrapper around the react-data-grid component. now i need a second component that is exactly the same except for one function (the rowGetter if you are familiar with react-data-grid.)

i work on an intranet so i will type a quick example...

function UsersInfoDataTable(props) {

  const userInfoCols = [
    {key: "a", name: "A", ...},
    {key: "b", name" "B", ...},
    {key: "c", name" "C", ...},
    ...
  ];

  const aCellCreation = (aData, description, index) => (
    <span title={...}>...</span>
  )

  const bCellCreation = (bData, index) => (
    <div className={...}>...</div>
  }

  ...

  
  const formattedTableData = props.myData.map(function (obj, index) {
    //do some stuff
    return {"a": aCellCreation(obj.a, obj.desc,index), "b": bCellCreation(obj.b, index), ...}
  });

  const rowGetter = rowNumber => formattedTableData[rowNumber];

  return (
    <div>
      <ReactDataGrid
        columns={userInfoCols}
        rowGetter={rowGetter}
        rowCount={formattedTableData.length}
        minHeight={props.minHeight}
       />
     </div>
   )
}

export default UsersInfoDataTable;

now i need something like this too...

function UsersInfoDataTableSlightlyDifferent(props) {

  //i need the same columns
  const userInfoCols = [
    {key: "a", name: "A", ...},
    {key: "b", name: "B", ...}
  ];

  //i need same cell format    
  const aCellCreation = (aData, description, index) => (
    <span title={...}>...</span>
  )

  //i need same cell format
  const bCellCreation = (bData, index) => (
    <div className={...}>...</div>
  }

  ...

  //the internals of this function will be pretty much the only difference
  const formattedTableData = props.myData.map(function (obj, index) {

    //do some different stuff than previous component as myData will be
    //in a different format when passed here versus the other component

    //the return will be essentially the same - maybe slightly different with
    //what is passed to the "creation" functions
    return {"a": aCellCreation(obj.a, obj.desc,index), "b": bCellCreation(obj.b, index), ...}
  });

  const rowGetter = rowNumber => formattedTableData[rowNumber];

  return (
    <div>
      <ReactDataGrid
        columns={userInfoCols}
        rowGetter={rowGetter}
        rowCount={formattedTableData.length}
        minHeight={500}
       />
     </div>
   )
}

export default UsersInfoDataTable;

A couple ways i thought to do what i want...

  1. Copy and paste with no reuse - bad
  2. Reuse the exact same component but check for data in one prop and if it exists loop and do stuff one way else loop through the other prop and do stuff different
  3. Change to use a React class instead of functional component and use inheritance (which i haven't had to do before so i'd have to do some reading)
  4. Change to use a React class and have all the functions be static and use them the static functions from one component in both components. ie. aCellCreation() and bCellCreation() would be static.

i'm kind of leaning towards #2 but what if i want to have essentially the same table and display user info from 4 different sources - it could get ugly. e.g. dataToMapThrough = props.allUsersData? props.allUserData : (props.usersFromUSAData? props.usersFromUSAData : ...)

Any suggestions on the best way to accomplish this?


UPDATE

Reading the document provided by Andy_D here is what i came up with if anyone is interested. Hopefully no typos since i had to hand type everything since i work on an intranet site...

function UsersInfoDataTableDisplayer(props) {

  const userInfoCols = [
    {key: "a", name: "A", ...},
    {key: "b", name: "B", ...}
  ];

  const aCellCreation = (aData, description, index) => (
    <span title={...}>...</span>
  )

  const bCellCreation = (bData, index) => (
    <div className={...}>...</div>
  }

  ...

  //data will already be processed exactly how this Component needs it every time
  //in the specialized version of this Component
  const formattedTableData = props.userInfoData.map(function (obj, index) {

    //Probably nothing to do here since data is processed in the specialized 
    //version of this component

    return {"a": aCellCreation(obj.a, obj.b,index), "b": bCellCreation(obj.b, index),"c": obj.c}
  });

  const rowGetter = rowNumber => formattedTableData[rowNumber];

  return (
    <div>
      <ReactDataGrid
        columns={userInfoCols}
        rowGetter={rowGetter}
        rowCount={formattedTableData.length}
        minHeight={500}
       />
     </div>
   )
}

export default UsersInfoDataTableDisplayer;

Now here is a specialized version #1...

function UsersInfoFromService1DataTable(props) {

  //The format of the JSON passed in is one way here 
  const formattedTableData = props.abcData.map(function (obj, index) {

    //"combine" and "clean" or whatever you need here
    let aLikeData = obj.firstName + ' ' + obj.lastName

    return {"a": aLikeData, "b": obj.description, "c": obj.whatever,...}
  });



  return (
    <div>
      <UsersInfoDataTableDisplayer
        userInfoData = {formattedTableData}
        minHeight={500}
       />
     </div>
   )
}

export default UsersInfoFromService1DataTable;

Here is specialized version #2...

function UsersInfoFromService1DataTable(props) {

  //The format of the JSON passed in is one way here 
  const formattedTableData = props.xyzData.map(function (obj, index) {

    //"combine" and "clean" or whatever you need here
    let aLikeData = obj.fullName

    return {"a": aLikeData, "b": obj.desc, "c": obj.something,...}
  });



  return (
    <div>
      <UsersInfoDataTableDisplayer
        userInfoData = {formattedTableData}
        minHeight={900}
       />
     </div>
   )
}

export default UsersInfoFromService2DataTable;

Answer

Andy_D picture Andy_D · May 31, 2017

The React docs address this question here: https://facebook.github.io/react/docs/composition-vs-inheritance.html

I'd read that and, within those guidelines, do whatever seems best.

TL;DR - don't use inheritance. But please do read.