recharts / recharts

Redefined chart library built with React and D3
http://recharts.org
MIT License
23.65k stars 1.69k forks source link

Chart components do not render when wrapped in custom components #2788

Open ziggy6792 opened 2 years ago

ziggy6792 commented 2 years ago

Reproduction link

Edit on CodeSandbox

Steps to reproduce

Open my minimal reproduction link

What is expected?

YAxis is displayed

What is actually happening?

YAxis is not displayed

Environment Info
Recharts v2.1.6
React 17.0.2
System Mac
Browser Chrome

I want to wrap chart components (like YAxis) up in my own components. I want to do this so that I can set some default behavior in my wrapper and then pass in some customisations.

For example

export const YAxisWrapper: React.FC<Props> = ({dataKey, label}) => (
  <YAxis dataKey={dataKey} allowDecimals={false} tick={tick}>
    <Label
      value={label}
      position='left'
      angle={-90}
      style={{ textAnchor: 'middle' }}
      offset={-25}
    />
  </YAxis>
);

However when I wrap rechats components up into my own components they are no longer displayed in the chart.

I have created a very simple sandbox example here. https://codesandbox.io/s/simple-bar-chart-forked-8f3jgg?file=/src/App.tsx

jake-daniels commented 2 years ago

+1 same issue here

Katli95 commented 2 years ago

Yeah, encountering the same issue here. I was very surprised to find out that this didn't work. It seems to me that this is a basic feature to be expected from a React library.

Could someone from the core team explain the preferred way to customize a control/component in a reusable way?

I guess my workaround for now is creating a hook which returns the props for the component I'm using.

bluestormmax commented 1 year ago

I've run into the same issue and tried the workarounds in https://github.com/recharts/recharts/issues/412 but haven't found a good solution yet.

@Katli95 Did you get this to work with the hook like you mentioned? If so could you paste an example or sandbox link?

gsipos commented 1 year ago

This is particularly painful because it makes hard to reuse bits and pieces between chart components, and I feel like I'm forced to repeat myself. For example I'm creating multiple variations of bar charts, that have basically everything(axis, tooltip, bars) styled according to app design, but the variants have a lot in common.

I've encountered this issue with XAxis, YAxis, Tooltip.

jake-daniels commented 1 year ago

@xile611 This issue is open for a quite long time now without any response from maintainers. Kind of a red flag.

@gsipos You can still create reusable pieces by creating function that returns JSX markup and then calling it in the "parent" markup.

function renderXAxis(params) {
   return (
      <some-markup>
   )
}   

// parent scope
return (
   <div>
      {renderXAxis(params)}
   </div>
)

But this approach lacks all the features of react component.

ckifer commented 1 year ago

Hi all,

recharts has been without active maintainers for a quite a while. Trying to get a team going again but it might take a while for stuff like this to be addressed, especially because we don't have the original creators.

Basically there is a giant "chart generator" function https://github.com/recharts/recharts/blob/master/src/chart/generateCategoricalChart.tsx that takes in axisComponents and define the axis types that can be children of that component. If an axis component is not a child of a chart component that goes through generateCategoricalChart then the axis has no data to use to render.

Without calling these functions within generateCategoricalChart - renderAxis/renderXAxis/renderYAxis the CartesianAxis component never gets returned because all YAxis and XAxis components are are some default props with a component that returns null

Not sure what the action item is here yet but this is the reason

ckifer commented 1 year ago

I haven't tried with just a Y-axis but we did add an example for the Customized example recently.

https://recharts.org/en-US/examples/CustomizedContent

Should help with some reusability at least.

Ian-Bright commented 1 year ago

Still no solution for this? Glad I found this issue because I've spent hours trying to figure out what the heck is wrong

ckifer commented 1 year ago

@Ian-Bright sorry you lost some time. I'll take an action item to add a disclaimer to the docs/storybook.

Without fundamentally changing how recharts works there is no way to fix this for now. In future state hopefully we are refactored enough that we can render things independent of the generate function

tran-simon commented 9 months ago

@Ian-Bright You can also override the class component

class MyYaxisComponent extends YAxis {
  render() {
    return <YAxis {...this.props} />;
  }
}

You can then pass custom props to the YAxis or change defaultProps

The only caveat is that you can't use hooks, only class components stuff. So to use a context you would use:

render(){
    return <MyContext.Consumer>
      {(value)=>{
        return (
          <YAxis {..this.props}/>
        )
      }}
    </MyContext.Consumer>
  }

https://codesandbox.io/s/simple-bar-chart-forked-2lsx9q?file=/src/App.tsx

[EDIT] Looks like YAxis is a Function component, so there is a type error and I haven't figured out how to override props?

I'll leave this comment as it might still be useful to override other recharts class-based components, I've got it working for PolarAngleAxis for example