mardi 1 novembre 2022

React -- Trigger chart download method from sibling component

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)

Aucun commentaire:

Enregistrer un commentaire