1

Looking for some guidance on how to structure the below components to trigger an (export) method in one component from another 'sibling' component.

Using a download button contained in <DownloadChartButton> in the Card Header, I want to download a chart contained in my PaginatedRechartsChart component as a PNG. I currently have the components structured like below. DownloadChartButton needs to access the recharts chart contained in my <PaginatedRechartsChart> component. Hoping to keep DashboardCard, DownloadChartButton, and PaginatedRechartsChart generic so they can be applied in many places, hence the current structure.

Question: Is there a better way to structure my components to avoid the React anti-pattern of triggering child methods from parent components?

Thank you for your help!

Pseudo-code:

export const LineChartByDay = () => {
  const [data, setData] = React.useState([{'date':'2021-01-01': 'count':34})
  return (
    <DashboardCard
      headerItems={<DownloadChartButton data={data}/>}
    >
      <PaginatedRechartsChart data={data}/>
    </DashboardCard>
  )
}
import {LineChart} from 'recharts'

export const PaginatedRechartsChart = (data) => {
  // ... extra code for client-side pagination, etc.
  return (
    <ResponsiveContainer>
       <LineChart data={data}/>
    </ResponsiveContainer>
  )
}
export const DashboardCard = ({ title, actions, children, error, loading }: { title: string, actions?: React.ReactNode, children?: React.ReactNode, error?: any, loading?: boolean }) =\> {
return (
  <Card>
    <CardHeader
      title={title}
      action={actions}
    ></CardHeader>
    <CardContent>
      {error && <ErrorAlertBar title={error}/>}
      {loading && <CircularProgress/>}
      {children}
    </CardContent>
  </Card>
)
}

export const DownloadChartButton = (data) => {
  // ... CSV download logic (takes a list of objects in data, exports to CSV)
  // ... PNG download logic
  
  const [getPng, { ref }] = useCurrentPng();
  
  //Ideally we'd have chart download logic in this component so we don't have to copy it into every chart component
  const handleDownload = React.useCallback(async () => {
    const png = await getPng();

    // Verify that png is not undefined
    if (png) {
      // Download with FileSaver
      FileSaver.saveAs(png, 'myChart.png');
    }
  }, [getPng]);

  return (
    <MenuButton>
      <MenuItem>CSV</MenuItem>
      <MenuItem>PNG</MenuItem>
    </MenuButton>
  )
}

From what I see online, my current thought is using a ref in the parent component and passing that to PaginatedRechartsChart and DownloadChartButton... but I'm seeing online that this is an anti-pattern for React (i.e. https://stackoverflow.com/a/37950970/20391795)

bcast
  • 11
  • 1

0 Answers0