ESLint React PropTypes with JSX - reactjs

I'm using the Airbnb javascript standards to lint my react app and i'm getting an error i'm not sure how to fix. I'm passing in another component in order to create a page about the component itself, including the component itself (children) a title and a markdown description. I get a bunch of errors for each of those items like this.
'children' is missing in props validation - react/prop-types
I can see that it is because i'm not defining props, but I can't figure out how to get it working.
I'm essentially outputting something like the below.
const example = ({ title, children, description }) => (
{title}
{md.render(description)}
{children}
)
How would I define my Props in this situation?

I ended up working this out, I just had typos in my original code! 😅
const example = ({ title, children, description }) => (
{title}
)
example.propTypes = {
title: PropTypes.string,
}

Related

How to enforce a strongly typed ReactElement as a React children? [duplicate]

I would like to only allow specific components as children. For example, let's say I have a Menu component, that should only contain MenuItem as children, like this:
<Menu>
<MenuItem />
<MenuItem />
</Menu>
So I would like Typescript to throw me an error in the IDE when I try to put another component as child. Something warning me that I should only use MenuItem as children. For example in this situation:
<Menu>
<div>My item</div>
<div>My item</div>
</Menu>
This thread is almost similar but does not include a TypeScript solution. I was wondering if the problem can be solved using TypeScript types and interfaces. In my imaginary world it would look like this, but of course the type checking is not working because the child component has an Element type:
type MenuItemType = typeof MenuItem;
interface IMenu {
children: MenuItemType[];
}
const MenuItem: React.FunctionComponent<IMenuItem> = ({ props }) => {
return (...)
}
const Menu: React.FunctionComponent<IMenu> = ({ props }) => {
return (
<nav>
{props.children}
</nav>
)
}
const App: React.FunctionComponent<IApp> = ({ props }) => {
return (
<Menu>
<MenuItem />
<MenuItem />
</Menu>
)
}
Is there a way to achieve this with Typescript? Like to extend the Element type with something related only to a specific component?
Or what would be a good approach for being sure that a child is an instance of a specific component? Without having to add condition that looks at the child component displayName.
To do that you need to extract props interface from children component (and preferably also parent) and use it this way:
interface ParentProps {
children: ReactElement<ChildrenProps> | Array<ReactElement<ChildrenProps>>;
}
so in your case it would look like this:
interface IMenu {
children: ReactElement<IMenuItem> | Array<ReactElement<IMenuItem>>;
}
const MenuItem: React.FunctionComponent<IMenuItem> = ({ props }) => {
return (...)
}
const Menu: React.FunctionComponent<IMenu> = ({ props }) => {
return (
<nav>
{props.children}
</nav>
)
}
Despite the answer above, you can't do this with children. You might do a runtime check in a dev build of your component, but you can't do this with TypeScript types — at least, not yet.
From TypeScript issue #18357:
Right now there's no way to specify what children representation is, except specifying ElementChildrenAttribute inside JSX namespace. It's heavily coupled with React representation for children which implies that children is a part of props. This makes impossible to enable type checking for children with implementations which store children separately, for instance https://github.com/dfilatov/vidom/wiki/Component-properties.
And note that it's referenced in #21699, where basically the possibly-breaking change around ReactElement may also make it possible to do this.
Right now, all you can do is that runtime check, or accept props (or arrays of props) and optionally a component function (in your case, you know it's MenuItem) and create the elements within your component.
There's also the question of whether you should do this. Why shouldn't I be able to write a component that returns a MenuItem rather than having to use MenuItem directly? :-)

What is the purpose of shouldForwardProp option in styled()?

I was able to put together that shouldForwardProp specifies which props should be forwarded to the wrapped element passed as an option to styled(), but I am having trouble finding a comprehensible example of its use case.
Is prop forwarding here akin to passing down props in React?
Why would one want to prevent certain props from being forwarded to the wrapped element while using styled()?
Forgive me for my ignorance or if my question lacks clarity - I am still learning MUI and attempting to wrap my head around it.
If you're using a built-in components like div or span and you want to allow the user to customize the styles via some props.
const MyComponent = styled('div')(({ bgColor }) => ({
backgroundColor: bgColor,
}));
When you're using it like this:
<MyComponent bgColor='red'>
The prop is passed to the real element in the DOM tree as attribute:
And react will complain, something like:
Warning: React does not recognize the `bgColor` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `bgcolor` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
This is why shouldForwardProp exists, to prevent styling props from being passed down and create invalid attribute:
const MyComponent = styled('div', {
shouldForwardProp: (props) => props !== 'bgColor',
})(({ bgColor }) => ({
backgroundColor: bgColor,
}));
Already great answer by #NearHuscarl!
If you are using TypeScript, I use utility function for it, so I always type prop names correctly:
export const shouldForwardProp = <CustomProps extends Record<string, unknown>>(
props: Array<keyof CustomProps>,
prop: PropertyKey,
): boolean => !props.includes(prop as string);
const MyComponent = styled('div', {
shouldForwardProp: (prop) => shouldForwardProp<MyComponentProps>(['isDisabled', 'bgColor'], prop),
})<MyComponentProps>(({ theme, isDisabled, size, bgColor }) => ({
...

React does not recognize the X prop on a DOM element

I am beginner developer and I am working on react (gatsby, TS, styled components) project. I am getting this error:
"React does not recognize the isOpen prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase isopen instead. If you accidentally passed it from a parent component, remove it from the DOM element."
export const Navigation = () => {
const [isNavigationOpen, setIsNavigationOpen] = useState(false);
const { isTablet } = useQuery();
const showNavbar = () => {
setIsNavigationOpen((previousState) => !previousState);
};
const renderElement = isTablet ? (
<>
<SvgStyled
src='bars_icon'
isOpen={isNavigationOpen}
onClick={showNavbar}
/>
<MobileNavigation isOpen={isNavigationOpen}>
{NAVIGATION_DATA.map(({ id, url, text }) => (
<LinkMobile key={id} to={url}>
<ExtraSmallParagraph>{text}</ExtraSmallParagraph>
</LinkMobile>
))}
</MobileNavigation>
</>
) : (
<FlexWrapper>
{NAVIGATION_DATA.map(({ id, url, text }) => (
<LinkDekstop key={id} to={url}>
<ExtraSmallParagraph>{text}</ExtraSmallParagraph>
</LinkDekstop>
))}
</FlexWrapper>
);
return renderElement;
};
I am sure that I am missing some fundamental react stuff or something. Maybe someone could help me and explain the reason of this error.
When this happens it is because all props passed to the styled component are then also passed down to the DOM element that you are styling.
You've likely a component that looks like the following:
const SvgStyled = styled(SVG)<{ isOpen: boolean }>`
// your CSS and logic referencing the `isOpen` prop
`;
To resolve this issue you refactor the styled component definition and explicitly pass only the props you want to the element being styled. Use an anonymous function component and destructure the prop you don't want to pass on to the DOM element, and spread the rest of the props. This ensures the className prop that styled-components is creating a CSS class for is passed through.
Example:
interface SvgStyledProps {
className?: string,
isOpen: boolean,
}
const SvgStyled = styled(({ isOpen, ...props}) => (
<Svg {...props} />
))<SvgStyledProps>`
// your CSS and logic referencing the `isOpen` prop
`;
For any other Typescript specifics/caveats with styled-components see docs.
As of styled components v5.1, you can alternatively prevent undesired props from being passed down to your React node by prefixing it with a dollar sign ($) and designating it as a transient prop:
const SvgStyled = styled(SVG)<{ $isOpen: boolean }>`
// your CSS and logic referencing the `$isOpen` prop
`;
// SVG does NOT receive props.$isOpen
docs

Only allow specific components as children in React and Typescript

I would like to only allow specific components as children. For example, let's say I have a Menu component, that should only contain MenuItem as children, like this:
<Menu>
<MenuItem />
<MenuItem />
</Menu>
So I would like Typescript to throw me an error in the IDE when I try to put another component as child. Something warning me that I should only use MenuItem as children. For example in this situation:
<Menu>
<div>My item</div>
<div>My item</div>
</Menu>
This thread is almost similar but does not include a TypeScript solution. I was wondering if the problem can be solved using TypeScript types and interfaces. In my imaginary world it would look like this, but of course the type checking is not working because the child component has an Element type:
type MenuItemType = typeof MenuItem;
interface IMenu {
children: MenuItemType[];
}
const MenuItem: React.FunctionComponent<IMenuItem> = ({ props }) => {
return (...)
}
const Menu: React.FunctionComponent<IMenu> = ({ props }) => {
return (
<nav>
{props.children}
</nav>
)
}
const App: React.FunctionComponent<IApp> = ({ props }) => {
return (
<Menu>
<MenuItem />
<MenuItem />
</Menu>
)
}
Is there a way to achieve this with Typescript? Like to extend the Element type with something related only to a specific component?
Or what would be a good approach for being sure that a child is an instance of a specific component? Without having to add condition that looks at the child component displayName.
To do that you need to extract props interface from children component (and preferably also parent) and use it this way:
interface ParentProps {
children: ReactElement<ChildrenProps> | Array<ReactElement<ChildrenProps>>;
}
so in your case it would look like this:
interface IMenu {
children: ReactElement<IMenuItem> | Array<ReactElement<IMenuItem>>;
}
const MenuItem: React.FunctionComponent<IMenuItem> = ({ props }) => {
return (...)
}
const Menu: React.FunctionComponent<IMenu> = ({ props }) => {
return (
<nav>
{props.children}
</nav>
)
}
Despite the answer above, you can't do this with children. You might do a runtime check in a dev build of your component, but you can't do this with TypeScript types — at least, not yet.
From TypeScript issue #18357:
Right now there's no way to specify what children representation is, except specifying ElementChildrenAttribute inside JSX namespace. It's heavily coupled with React representation for children which implies that children is a part of props. This makes impossible to enable type checking for children with implementations which store children separately, for instance https://github.com/dfilatov/vidom/wiki/Component-properties.
And note that it's referenced in #21699, where basically the possibly-breaking change around ReactElement may also make it possible to do this.
Right now, all you can do is that runtime check, or accept props (or arrays of props) and optionally a component function (in your case, you know it's MenuItem) and create the elements within your component.
There's also the question of whether you should do this. Why shouldn't I be able to write a component that returns a MenuItem rather than having to use MenuItem directly? :-)

React Unknown Prop Warning

I was trying to render a DOM element with a prop but my attempt is not recognized by React as a legal DOM attribute/property.
So I started researching for a solution and I found this source from React Warning Documentation. However, although well explained, I have a deeper problem that will involve a deeper solution therefore.
As an example of code I will introduce the ExampleContainer method:
export let ExampleContainer = (props) => {
return (
<DetailsContainer tabs={props.tabs} activeTab={props.activeTab}>
{props.children}
</DetailsContainer>
)
}
Now I have my mapStateToProps receiving a statethat I beleave is well implemented and I will bring it here just to context my problem:
const mapStateToProps = (state) => {
return {
i18n: state.i18n,
activeTab : state.example.activeTab
}
}
Finally, the problem is inside my mergeProps where I have this tabs source giving me the problem and more specificly inside it on the i18n attribute:
const mergeProps = (stateProps, dispatchProps, ownProps) => {
return Object.assign({}, ownProps, {
tabs: [
{
'onClick' : dispatchProps.goToExampleOptionA,
'key' : 'ExampleOptionA',
'i18n' : stateProps.i18n.Tabs.exampleOptionATab
},
{
'onClick' : dispatchProps.goToExampleOptionB,
'key' : 'ExampleOptionB',
'i18n' : stateProps.i18n.Tabs.exampleOptionBTab
}
]
}, stateProps)
}
The problem is when I open my Container it brings to me this warning:
And my DeyailsContainer component is this one:
let DetailsContainer = ({
tabs,
rightIcons,
activeTab,
children
}) => {
return (
<div>
<Tabs tabs={tabs} rightIcons={rightIcons} activeTab={activeTab}/>
<div className="app-wrapper">
{children}
</div>
</div>
)
}
This is due to passing your i18n prop directly into a <div i18n="whatever" /> somewhere.
My guess is it's inside your DetailsContainer component. If you're spreading all of the props your component receives into the div like this <div {...props} />, that will definitely do it.
React has warnings now when you try to pass some type of prop that isn't a standard attribute for a DOM element. If you need the i18n as an attribute for a reason like this... HTML tags in i18next translation, you'll want to change that to data-i18n as the prop.

Resources