react-component/collapse

How to render wrapped Panel under Collapse?

homura opened this issue ยท 7 comments

// MyPanel.jsx
return (
  <Collapse.Panel>
    ...
  </Collapse.Panel>
)

// List.jsx
return (
  <Collapse>
    {xs.map((x) => <MyPanel {...x} />)}
  </Collapse>
)

Since List.jsx and MyPanel.jsx contains complex business, spilt Collapse and Panel into two files.But it doesn't work.

So how to render wrapped Panel under Collapse?

Only Collapse.Panel can be the children of Collapse.

If you want to extract common logic, try:

function somePanelGenerator(props) {
  return <Collapse.Panel {...props} />
}

<Collapse>
  {somePanelGenerator(props)}
</Collapse>

@benjycui Thanks for you answer.

This would be a great addition to the docs.

I spent some time on that issue... cause special function is anti-pattern for me

and It's doable ๐ŸŽ‰ , you just need to pass rest props to ie. Collapse.Panel (check source code of rc-panel to understand it)

export const CustomPanel: React.FC = ({ header, children, ...props }) => {
  // do whatever you like

  return <Collapse.Panel header={header} {...props}>
    {children}
  </Collapse.Panel>
};

and then use it like that:

<Collapse>
    <CustomPanel key="first" header="Header 1">Body</CustomPanel>
    <CustomPanel key="second" header="Header 2">Body</CustomPanel>
</Collapse>

If you're using typescript both hoc and Panel types are not typed correctly so you'll need to pass at least isActive, openProps and key props

If you're using typescript both hoc and Panel types are not typed correctly so you'll need to pass at least isActive, openProps and key props

Just to expand a little on this for the Typescript users out there:

If you want to wrap inner components like Collapse.Panel with custom behaviour, all you have to do is add CollapsePanelProps to the props definition of your wrapper component and then spread the props in the actual Panel component that is returned. i.e -

import {  CollapsePanelProps, Panel } from "antd"

type PanelWrapperProps = {
  // your panel wrapper props
} & CollapsePanelProps

const PanelWrapper = (props: PanelWrapperProps) => {
  return (
     <Panel {...props} />
  )
}

You can see what's happening under the hood here: The Collapse clones all of its direct children (which it expects to be Panel) and injects additional props into them. So all you need to do is allow any arbitrary direct child of Collapse to accept those props.

You can also override whichever props you want like isActive, just make sure you add it after the {...props} spread. This will allow you to have granular control over the Panel's behaviour. I.e -

const PanelWrapper = (props: PanelWrapperProps) => {
  const [state, setState] = useState<{isActive:boolean}>({isActive:false})
  return (
     <Panel {...props}  isActive={state.isActive} onItemClick={()=>{setState((prev) => !prev.isActive)}} />
  )
}

We have a prop name "forceRender" that we can pass inside the Panel component of Collapse, it will help to render content inside Panel without clicking on the header.

<Panel header="Some header name" key="1" forceRender>
   Content goes here
</Panel>