Closed benjycui closed 6 months ago
It's difficult, for some components use key
as part of API, we cannot fix this until we rename all the API's names. e.g.
key => id
expandedKeys => expandedIds
selectedKeys => selectedIds
....
It will be a breaking change, but such kind of breaking change can be resolve by antd-codemod.
SO, do you think that it is worthy doing so?
I don't think this is a good idea.
Not fan of codemod solution. Just my point of view, but, I believe that sadly for most of people it is more repulsive than welcomed.
Embedding components into custom components is a common practice in React.
Please fix it and see ant-design adoption fast increase with no doubt.
Ant Design is really appealing and is the first library that makes me want to leave react-bootstrap.
@MacKentoch
It's difficult, for some components use key as part of API, we cannot fix this until we rename all the API's names.
@afc163 if we cannot rename those APIs, then we can not resolve this problem. But we can provide a work around, see: https://github.com/react-component/collapse/issues/73#issuecomment-323626120
Do you think that we should add this to documentation?
@benjycui I understand.
Anyway, it is not blocking after all.
Thank you for taking time answering.
@benjycui I was investigating the workaround you proposed but I think it is not a proper solution. Usually when you wrap a component you want also to have some internal state. With the solution proposed this is not possible. Also I think this is not a little problem. Not being able to isolate common components means having the same code repeated lot of times inside an application. If the application is big I would consider not to adopt antd at all. Please consider this as a constructive critic. Thanks for your work!
Agreed to say this is not a small problem and it's something I did not expect of when I started using Ant Design library, the good practice of custom components are used throughout React projects. Personally, I really like Ant Design, but for some, this could be a dealbreaker. Would really love to see this gets improved in the upcoming Ant Design v3.
Please find a solution for this in v3.
just ran into this trying (sorry if this is wrong) to create a navbar using menu and nesting React Router <Link />
tags in the Menu with <Icon />
.
Is there a more preferred solution?
IMHO, this thread should be in the official docs, because this issue is likely to be a dealbreaker for many users. The docs should also mention https://github.com/react-component/collapse/issues/73#issuecomment-323626120 as an alternative when no state is needed.
I would have definitely appreciated mention of this in the documentation! I was trying to do something like this and wasted quite a bit of time because it doesn't work.
<Collapse>
<MyCollapseItem />
<MyCollapseItem2 />
</Collapse>
Where MyCollapseItem
& MyCollapseItem2
render the Collapse.Panel
.
Also, now that react16 allows you to return arrays of components from render, being able to do this would be especially helpful. Otherwise, prevent duplicate code is challenging.
Any updates on this?
This is actually a major issue for us. I'd vote for the renaming breaking change, as there is no workaround I can think of.
Same here - I need that for Tabs.TabPane
and Menu.MenuItem
.
I found a workaround basically you can pass the rest of the props with <Tabs.TabPane {...props} /> from the wrapped component.
If you use with React.Children.map(children, (child, index) => { ... })
the workaround doesn't work, at least for me. In the end, the key property gets a value like mykey/.0
which is not what I can work. I'd also vote for a name change on the properties.
this issue may actually drive me away from antd... Or at least cause me to find a replacement for these components. It really limits your ability to create reusable components.
For anyone trying to utilize React Router within a Menu.Item @yunshuipiao had a successful solution that worked for me in #6576
Another small issue: wrapping Menu.Item also prevents the Submenu from appearing selected when one of its child items is selected, at least in vertical mode. Relevant lines:
https://github.com/react-component/menu/blob/018d6f84d622ee140d9695ba57e7a773cf368efa/src/util.js#L40 https://github.com/react-component/menu/blob/018d6f84d622ee140d9695ba57e7a773cf368efa/src/SubMenu.jsx#L314
Thought I'ld document this for those who are trying to make Collapse work with a custom component: Similarly to what @jfreixa mentioned, just pass all the props given to the custom component to the Panel, eg
<Collapse>
<Custom/>
<Custom/>
</Collapse>
Custom.render() {
return (
<Panel {...this.props}>
<div>My custom stuff here</div>
</Panel>
)
}
Same issue than @ncknuna.
Menu.Item are not selected when wrapped. Is there a workaround?
@Nomeyho I ended up reconstructing the logic that determines whether the ant-menu-submenu-selected
class gets added by copy/pasting the relevant methods and commenting out the original check, then passing the class as className
in my wrapper:
function loopMenuItemRecusively (children: Array<any>, keys: Array<string>, ret: { find: boolean }) {
if (!children || ret.find) {
return
}
React.Children.forEach(children, (c: any) => {
if (ret.find) {
return
}
if (c) {
const construt = c.type
// Original check that caused problems. I'm not concerned about omitting it
// because I don't plan on putting a bunch of weird, large stuff in my menus...
// if (!construt || !(construt.isSubMenu || construt.isMenuItem || construt.isMenuItemGroup)) {
// return;
// }
if (keys.indexOf(c.key) !== -1) {
ret.find = true
} else if (c.props && c.props.children) {
loopMenuItemRecusively(c.props.children, keys, ret)
}
}
})
}
function isChildrenSelected (children: Array<any>, selectedKeys: Array<string>) {
const ret = { find: false }
loopMenuItemRecusively(children, selectedKeys, ret)
return ret.find
}
// Omitting other custom code below...
export const SubMenu = ({ children, className, selectedKeys, title, ...rest }: any) => {
const isSelected = isChildrenSelected(children, selectedKeys)
className = [
className,
isSelected ? 'ant-menu-submenu-selected' : ''
].filter(className => classname).join(' ')
return (
<SubMenu title={title} className={className} selectedKeys={selectedKeys} {...rest}>
{children}
</SubMenu>
)
}
Is there any fix for this?
I'm agree with @ChuckJonas
this issue may actually drive me away from antd... Or at least cause me to find a replacement for these components. It really limits your ability to create reusable components.
I need to use Menu SubMenu and Menu.items like this: Why? because i can use my "CustomSubMenu" elements in other pages... this is an important part of "reusable" components.
MainFile.js
Import CustomSubMenu from './OtherFile.js';
<Menu>
<CustomSubMenu />
<CustomSubMenu2 />
<CustomSubMenu3 />
</Menu>
and OtherFile.js like this:
render(){
return(
<SubMenu>
<SubMenu.item />
<SubMenu.item2 />
etc...etc...
</SubMenu>
);
}
Temporary workaround (edited for simplicity) for Submenus:
const SubMenuArray = ({ ...props }) =>
[1, 2].map(i => (
<Menu.SubMenu {...props} eventKey={`item_${i}`} subMenuKey={`${i}-menu-`} />
));
When dealing with arrays:
eventKey
and subMenuKey
, which should be uniqueA better approach would probably be:
const SubMenuWrapper = ({ children, ...props }) =>
React.Children.map(children, (child, i) =>
React.cloneElement(child, {
...props,
eventKey: `item_${i}`,
subMenuKey: `${i}-menu-`
})
);
Usage:
<Menu>
<SubMenuWrapper>
<CustomSubMenu title={"one"}/>
<CustomSubMenu title={"two"}/>
</SubMenuWrapper>
</Menu>
Again, you probably do not want to use the index in the array, so don't use this in production, but the idea is solid.
I think we have a way to remove the dependency of the key. Take Menu as an example, we can introduce an itemKey
prop and then use the context
to implement the Menu. In order to maintain compatibility, the Menu still traverses the children and change the key
to itemKey
if it presents. At the same time, we can also keep the semantics of props such as selectedKeys
.
@yesmeck to be honest it makes a while since I don't use ant design
(but plan to use it for an important application within this week).
As far as I understand, you could benefit the react new context API
as workaround?
That is great news
Yes, we need to context
instead of cloneElement
to solve the problem.
I think a solution is not to use "React.Children.forEach" and "React.cloneElement" to pass props and set new props, use a function, for example to custom:
<Select>
{({ onSelect }) => (
<div>
<Option onClick={onSelect} key="0">option1</Option>
<Option onClick={onSelect} key="1">option2</Option>
</div>
)
</Select>
and antd select source also use function children props rather than "React.Children.forEach" and "React.cloneElement"
Excuse me if this is a stupid question, but I am still fairly new to React and Ant Design. Does this mean that practically we can't use Ant design menus inside a react-redux connected SPA? If so, how can you write a relatively complex SPA with Ant Design? Is there a workaround?
Any update on this?
is there any update about this issue ? Menu item is behaving weird with HOC.
Hi ! Same here, I'm really interested in being able to customize these kind of components.
Actually having the problem with a custom Select.Option
No workaround proposed in this thread helped me make it work, I have a working select with empty options....
import React from 'react';
import PropTypes from 'prop-types';
import { Select } from 'antd';
const { Option } = Select;
const PictureOption = ({ label, value, pictureId, ...props }) => (
<Option label={label} value={value} className="select-item" {...props}>
<div className="select-item__thumbnail">
<img src={pictureId} alt="item-img" />
</div>
<div className="select-item__name">{label}</div>
</Option>
);
PictureOption.propTypes = {
label: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
pictureId: PropTypes.string.isRequired,
};
export default PictureOption;
I started to use CASL to render UI elements based on user permission. However I ran into the problem that SubMenus are not rendered because the the call to the isRootMenu function fails due to the fact that the props are not correctly propagated to the child elements.
As a workaround I defined the SubMenus as constants and passed in the props 'by hand':
render() {
// ### Administration Menu ###
const AdminMenu = ({ ...props }) => {
return (
<Can I="access" on="admin-content">
<Menu.Divider {...props} />
<Menu.SubMenu
{...props}
title={
// FIXME: Icon is not rendered... :-/
<span>
<Icon type="tools" />
<span>Administration</span>
</span>
}
// title={<span>Administration</span>}
key="admin">
<Menu.Item key="users" tabIndex={0}>
<Icon type="android" />
<span>User Administration</span>
</Menu.Item>
<Menu.Item key="permissions" tabIndex={0}>
<Icon type="lock" />
<span>Permissions</span>
</Menu.Item>
</Menu.SubMenu>
</Can>
);
};
return (
<Layout id="menu-component-layout">
<Layout.Sider width="300px" collapsible="false" trigger={null}>
<Menu theme="dark" mode="inline" defaultSelectedKeys={['user']} defaultOpenKeys={['user', 'config', 'admin']}>
<AdminMenu />
</Menu>
</Layout.Sider>
<Layout.Content id="menu-component-content">
<h3>Page containing a side menu</h3>
</Layout.Content>
</Layout>
);
}
This solution is not very handy but it works for now. However I still have the problem that the title of the SubMenu which includes an icon is not correctly rendered. The icon is missing.
Does anyone has an idea how to fix that?
I created a showcase here: https://github.com/gibelium/meteor-react-antd-casl
GitHubShowcase for the usage of CASL authorization library in a meteor/react environment using the ant-design UI library - gibelium/meteor-react-antd-casl
@gibelium I think that icon rendering deserves its own issue actually. I cloned your repo and tried replacing the icon with a ghost button and the button outline is visible, but the icon also does not render in/on the button...
@gotjoshua will you create that dedicated issue?
Setting the default expanded menu items also does not work. My workaround implementation ignores the defaultOpenKeys
property of Menu.
Any ideas how to solve that?
This issue should really be mentioned in the docs, I wasted a full workday researching this bug only to understand this library is unusable for me. Please please please put a warning so other developers won't have to waste so much time figuring out you used an anti-pattern, relaying on React keys, and now you are very fragile to so many HOC or decorators available to the community. This is really a shame. I hope you'll fix it in your next major version.
is there any update about this issue ? Set Menu defaultOpenKeys not working
Totally something that should be really fast considered as high priority. 🔥
I have similar use-case which I am (probably) not able to implement.
I would like to create redux connected component which renders Select
with options based on data in redux store. As I dont want to "copy-paste" the same code everywhere, I would like to use this type of component inside Form.getFieldDecorator
, but due to using of connect
HOC I am not able to do it.
EDIT: I found the solution for my use-case. I was able to create such a component as described above with forwardRef
option like this:
connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })(Component);
This solution is specific for connect
HOC, but you should be able to create similar solution by using React.forwardRef.
In addition to previous comments - yes I also think that this could be considered as high priority. After I successfully solved one of my issues (as I described above), now I would need to create Tabs.TabPane
component wrapped with my custom component. I have very common use-case -> wrapping component is used to check permissions, so if conditions are met, child component is rendered, otherwise not.
Is there any simple and working workaround for this?
any updates on this please?
I found a workaround basically you can pass the rest of the props with <Tabs.TabPane {...props} /> from the wrapped component.
This is showing console warnings Any way to solve this?
index.js:1437 Warning: React does not recognize the
staticContextprop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase
staticcontext` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
index.js:1437 Warning: Invalid value for prop dispatch
on
same problem... I want to create a permission component as HOC to wrap Menu.Item but antd does not allow this... sad
I spent some time on that issue...
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>
It is not a common use case, so it's low priority. But we still can discuss and try to make it better:
Menu
Menu.SubMenu
Menu.MenuItem
, cannot be wrapped for keyboard operations rely oninstance.onKeyDown
https://github.com/ant-design/ant-design/issues/4849Table.Column
, forTable
will readchildren[n].props
as config and it cannot not read children's ownee's props now https://github.com/ant-design/ant-design/issues/4029 https://github.com/ant-design/ant-design/issues/4324Collpase.Panel
, forCollapse
will readchildren[n].header
as title.Tabs.TabPane
, forTabs
will readchildren[n].tab
to generate TabBar. https://github.com/ant-design/ant-design/issues/5165Select.Option
, forSelect
depends onkey
prop on Option.Descriptions.Item
https://github.com/ant-design/ant-design/issues/27411