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
Related
I'm trying to implement a simple dark/light theme toggle to my website. In my base App.tsx I've implemented the state I use:
const [colorScheme, setColorScheme] = useState("light");
I pass that "colorScheme" variable as a prop to every other element. The theme toggle is contained in a header element, so I also pass the "setColorScheme" function to header as a prop. Within Header.tsx, the code triggered when the toggle is clicked is:
setColorScheme(s => s === "dark" ? "light" : "dark");
Within every specific element, I set the color scheme like so:
<ElementName className={"element_name element_name_"+colorScheme}/>
I have all the css for styling the component contained in the class "element_name", and then all relevant color data is contained in "element_name_light" or "element_name_dark".
When the toggle in the header is clicked, a re-render is triggered for the main body of the app, and for the header. But all of the other elements do not re-render. If I navigate to another element, the re-render happens and the color scheme appears as intended.
Attached is a gif of this happening.
I'm still learning React, so I'm sure it's something obvious I'm missing. I would appreciate any tips anyone can provide! Thanks
One note: I am using react functionally, rather than implementing classes for each component.
It's impossible to tell exactly what mistake you made since you haven't shared your code. But I can tell you the root mistake is not using React's context API. This will allow you to hold the color scheme and the toggle function as a global state and import them into every component via the useContext hook.
Here's an example on stackblitz: https://stackblitz.com/edit/react-ts-lhwstv?file=color-scheme-ctx.tsx
Here's the docs: https://reactjs.org/docs/context.html
Note: I'm using typescript, if you're using plain javascript just remove the type declarations and the generic typings <Type>.
You start by creating the context and giving a default value:
type ColorScheme = 'light' | 'dark';
type Props = { colorScheme: ColorScheme; toggleColorScheme: () => void };
export const ColorSchemeCtx = createContext<Props>({
colorScheme: 'light',
toggleColorScheme: () => {},
});
I like to then create a provider component for organization.
export const ColorSchemeCtxProvider: FC<PropsWithChildren<{}>> = ({
children,
}) => {
const [colorScheme, setColorScheme] = useState<ColorScheme>('light');
function toggleColorScheme() {
setColorScheme((s) => (s === 'dark' ? 'light' : 'dark'));
}
return (
<ColorSchemeCtx.Provider value={{ colorScheme, toggleColorScheme }}>
{children}
</ColorSchemeCtx.Provider>
);
};
Then wrap all components that need the context - probably just put it at the highest level possible.
root.render(
<StrictMode>
<ColorSchemeCtxProvider>
<App />
</ColorSchemeCtxProvider>
</StrictMode>
);
Now any component can get both the color scheme and / or the toggle function with useContext
export default function App() {
const { colorScheme, toggleColorScheme } = useContext(ColorSchemeCtx);
return (
<div>
<p>The color scheme is: {colorScheme}</p>
<button onClick={toggleColorScheme}>TOGGLE</button>
<CompOne />
<CompTwo />
<CompThree />
</div>
);
}
export default function CompOne() {
const { colorScheme } = useContext(ColorSchemeCtx);
return <div className={'comp-one ' + colorScheme}></div>;
}
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 }) => ({
...
I am trying to do a custom label for my barchart using <LabelList> component.
This is my LabelList component and the corresponding render content function:
<LabelList
dataKey='pv'
position='insideRight'
fill='#f5f5f5'
className={'chart-label'}
offset={15}
content={renderLabel}
/>
const renderLabel = function(props: LabelProps){
return `${props.value}%`;
};
I just need to append a % symbol at the end of the chart value. but this is not working.
I used formatter prop for <LabelList> component instead of content prop.
<LabelList
dataKey='pv'
position='insideRight'
fill='#f5f5f5'
className={'chart-label'}
offset={15}
formatter={renderLabel}
/>
const renderLabel = (props: any) => {
return `${props}%`;
};
Yes using formatter for this is correct when you just want to affect the raw label text. The content property/api should be used if you want to provide the full markup for the label itself with full freedom to do what ever you like, and can be set to another react component, or to a function which must return the result from JSX rendering
Heres an example for other who stumbles across this blog and might need to use the 'content' freedom.
Use LabelList with content set to function which returns rendered JSX
<LabelList
content={renderLabelContent}
/>
The function:
const renderLabelContent = (props: object) => {
const { x: number, y, width, height, value } = props;
return (
<div>
{// All kinds of custom JSX/html here}
{value}
</div>
);
};
Using it with another selfmade react component (CustomizedLabel), you can pass in properties and values;
<LabelList
content={<CustomizedLabel external={external} />}
/>
I'm trying to create a wrapper component around the react-router-dom NavLink component.
I would like my custom component to accept all of NavLinks props, and proxy them down to NavLink.
However when I do this, I'm getting:
Warning: React does not recognize the staticContext prop 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.
A working demo of the issue can be found here:
https://codesandbox.io/s/w0n49rw7kw
There is a way to overcome that is using:
const { to, staticContext, ...rest } = this.props;
So your ...rest will never contain staticContext
This is a common problem with a simple solution as documented in the React documentation:
The unknown-prop warning will fire if you attempt to render a DOM
element with a prop that is not recognized by React as a legal DOM
attribute/property. You should ensure that your DOM elements do not
have spurious props floating around.
The spread operator can be used to pull variables off props, and put
the remaining props into a variable.
function MyDiv(props) {
const { layout, ...rest } = props
if (layout === 'horizontal') {
return <div {...rest} style={getHorizontalStyle()} />
} else {
return <div {...rest} style={getVerticalStyle()} />
}
}
You can also assign the props to a new object and delete the keys that
you’re using from the new object. Be sure not to delete the props from
the original this.props object, since that object should be considered
immutable.
function MyDiv(props) {
const divProps = Object.assign({}, props);
delete divProps.layout;
if (props.layout === 'horizontal') {
return <div {...divProps} style={getHorizontalStyle()} />
} else {
return <div {...divProps} style={getVerticalStyle()} />
}
}
This happens because you probably used {...props} somewhere in your component.
Example from React:
function MyDiv(props) {
const { layout, ...rest } = props
if (layout === 'horizontal') {
return <div {...rest} style={getHorizontalStyle()} />
} else {
return <div {...rest} style={getVerticalStyle()} />
}
}
We grab layout separately so that it won't be contained in {...rest}.
The given answer by the React docs was not quite good enough for my situation, so I found/developed one which isn't perfect, but is at least not so much of a hassle.
You can see the Q/A in which it arose here:
What is Reacts function for checking if a property applies?
The gist is, use a function to pick the bad props out for you.
const SPECIAL_PROPS = [
"key",
"children",
"dangerouslySetInnerHTML",
];
const defaultTester = document.createElement("div")
function filterBadProps(props: any, tester: HTMLElement = defaultTester) {
if(process.env.NODE_ENV !== 'development') { return props; }
// filter out any keys which don't exist in reacts special props, or the tester.
const out: any = {};
Object.keys(props).filter((propName) =>
(propName in tester) || (propName.toLowerCase() in tester) || SPECIAL_PROPS.includes(propName)
).forEach((key) => out[key] = props[key]);
return out;
}
Personally, I felt that the warning was completely useless in the first place, so I added a line which skips the check entirely when not in development mode (and warnings are suppressed). If you feel that the warnings have merit, just remove the line:
if(process.env.NODE_ENV !== 'development') { return props; }
You can use it like this:
public render() {
const tooManyProps = this.props;
const justTheRightPropsForDiv = filterBadProps(tooManyProps);
const justTheRightPropsForSpan = filterBadProps(tooManyProps, document.createElement("span"));
return (<div {...justTheRightPropsForDiv}>
<span {...justTheRightPropsForSpan} />
</div>)
}
If someone has this issue with react-admin, check if you don't have a Link as a child of Admin. Like this:
<Admin layout={props => <Layout/>}>
<Link to="/something">something</Link> <-- causing issue
</Admin>
Just move it to another component. For instance, inside the Layout.
I got the same issue when passing data in child component with camelCase property.
Warning: React does not recognize the moreInfo prop on a DOM element.
If you intentionally want it to appear in the DOM as a custom attribute,
spell it as lowercase moreinfo instead. If you accidentally passed it
from a parent component, remove it from the DOM element.
<CenteredModal
moreInfo={viewType}
/>
To fix that error, I used all lowercase letters for property.
<CenteredModal
moreinfo={viewType}
/>
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.