The typing of my style properties is in error - javascript

In my component, I receive props of styles, like this:
export const NavBox = styled.div<{ open: boolean }>`
display: flex;
height: 100%;
justify-content: flex-end;
align-items: center;
#media (max-width: 768px) {
flex-direction: column;
position: fixed;
width: 100%;
justify-content: flex-start;
background-color: var(--color);
transition: all 0.3s ease-in;
top: 10.3vh;
padding-top: 10vh;
left: ${(props) => (props.open ? '-100%' : '0')};
}
`
And in Header, I receive this error about types:
Property 'navbarOpen' does not exist on type 'IntrinsicAttributes & Omit<Omit<Pick<DetailedHTMLProps<HTMLAttributes, HTMLDivElement>, "key" | keyof HTMLAttributes<...>> & { ...; },
Property 'open' is missing in type '{ children: Element; }' but required in type 'Omit<Omit<Pick<DetailedHTMLProps<HTMLAttributes, HTMLDivElement>, "key" | keyof HTMLAttributes> & { ...; } & { ...; }, never> & Partial<...>, "theme">'.
This is the code:
const Header = () => {
const [navbarOpen, setNavbarOpen] = React.useState<boolean>(false)
return (
<S.HeaderWrapper>
<S.NavWrapper>
<S.Toggle
navbarOpen={navbarOpen}
onClick={() => setNavbarOpen(!navbarOpen)}
>
{navbarOpen ? <S.Hamburger open /> : <S.Hamburger />}
</S.Toggle>
{navbarOpen ? (
<S.NavBox>
<NavBar />
</S.NavBox>
) : (
<S.NavBox open>
<NavBar />
</S.NavBox>
)}
</S.NavWrapper>
</S.HeaderWrapper>
)
}
Anyone can help me with this problem? Thanks for any help!

You defined type for "NavBox" with required property with name "open", but not passed it. You need make it non-required or pass it always.
I don't see types for "Toggle" and "Hamburger". But they is highlighted. Check their. Check property names in type and need their property always or sometimes.
If you need property always:
interface Some {
isOpen: boolean;
}
If you need property sometimes:
interface Some {
isOpen?: boolean;
}
Below, i highlight problem places
const Header = () => {
const [navbarOpen, setNavbarOpen] = React.useState<boolean>(false)
return (
<S.HeaderWrapper>
<S.NavWrapper>
<S.Toggle
navbarOpen={navbarOpen} // Check navbarOpen property in "Toggle" type
onClick={() => setNavbarOpen(!navbarOpen)}
>
{navbarOpen ? <S.Hamburger open /> : <S.Hamburger />} // Check required or non-required "open" property in "Hamburger" component
</S.Toggle>
{navbarOpen ? (
<S.NavBox> // You set "open" as required property, you need pass "open" property.
<NavBar />
</S.NavBox>
) : (
<S.NavBox open>
<NavBar />
</S.NavBox>
)}
</S.NavWrapper>
</S.HeaderWrapper>
)
}
I made a little refactoring. In example below. "Open" property is required, pass it always. And renamed prop for "Toggle" component.
const Header: React.FC = () => {
const [navbarOpen, setNavbarOpen] = useState<boolean>(false)
const handleClick = useCallback(() => {
setNavbarOpen(!navbarOpen)
}, [navbarOpen])
return (
<S.HeaderWrapper>
<S.NavWrapper>
<S.Toggle open={navbarOpen} onClick={handleClick}>
<S.Hamburger open={navbarOpen} />
</S.Toggle>
<S.NavBox open={navbarOpen} >
<NavBar />
</S.NavBox>
</S.NavWrapper>
</S.HeaderWrapper>
)
}

Related

Console.Logging Mapped Data in React

It appears I cannot console.log any selection from an onClick for this React component. I am trying to basically achieve the following.
https://codesandbox.io/s/great-ardinghelli-6seko?file=/src/demoComponent.jsx
The data is fetched from redux and appears in the frontend (I have this working).
I would like to achieve that when you select a line, it just console.logs('you have selected'role).
However it doesn't appear I can get ANY data to trigger on an onClick when it's a mapped data source.
I have implemented a p tag which fires and works as expected.
See the following refined code:
import React, { useRef } from "react";
import styled from "styled-components";
const SearchContent = styled.div`
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
padding: 1em;
overflow-y: auto;
`;
const JobContainer = styled.div`
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
padding: 5px 5px;
`;
export function SearchBarTrialRedux(props) {
const inputRef = useRef();
const [term, setTerm] = useState("")
const handleClickRole = () => {
console.log("Hello, you have selected" )
}
const searchResults = useSelector(state => state.jobsearch.roles.map(role => role.title))
const SearchResultsText = ({ text }) => {
return (
<JobContainer onClick={()=> {
console.log("Hello")
}}>
<JobSearchTypography
>
{text}
</JobSearchTypography>
</JobContainer>
);
};
return (
<SearchContent>
<JobContainer >
{searchResults && searchResults.length < 3 && searchResults.map((result) =>
(<IndividualContainer handleClick={handleClickRole}>
<SearchResultsText text={result}/>
</IndividualContainer>
)
)}
</JobContainer>
<p onClick={handleClickRole}>
fff
</p>
</SearchContent>
)
Does anyone know why this doesn't work + how to achieve the solution? I'm a little lost here!
As it can be seen from your code snippet the handleClickRole function just prints to the console the message "Hello, you have selected". There is no variable passed on it in order to print it.
This can be changed like this:
const handleClickRole = (role) => {
console.log(`Hello, you have selected ${role}`);
}
Furthermore, the way you call the above function on the onClick event I think is incomplete. It would be better if it looked like this:
<p onClick={() => handleClickRole(role)}>fff</p> // considering that you want to pass a variable with the role
or
<p onClick={() => handleClickRole()}>fff</p> // without a variable
Please also look at the below example:
import { useState } from "react";
export default function App() {
const [myInput, setMyInput] = useState("");
const [isButtonClicked, setIsButtonClicked] = useState(false);
const handleChange = (event) => {
setIsButtonClicked(false);
setMyInput(event.target.value);
};
const handleClick = () => {
setIsButtonClicked(true);
console.log(myInput);
};
return (
<div className="App">
<input
onChange={handleChange}
id=".myInput"
placeHolder="Enter Something"
/>
<button
style={{
display: "block",
width: "100px",
margin: "10px auto",
}}
onClick={() => handleClick()}
>
OK
</button>
<p style={{ fontWeight: "bold" }}>
The entered value is:
<span style={{ fontWeight: "400" }}>
{isButtonClicked ? ` ${myInput}` : null}
</span>
</p>
</div>
);
}
the <IndividualContainer>...</IndividualContainer> is a component created by you right?
if yes, then you will have to have to call the onClick function in the component like so:
inside the IndividualContainer component
...
// the outer container
<div onClick={props.handleClick}>
...
</div>
...
lemme know if it works
I just edited my answer now

No overload matches this call with styled components

I am trying to create a button component using styled components that takes a prop as which can be a ComponentType | string When I try another prop called skin or size I get the error No overload matches this call. I have googled what I can think of under the sun. I have tried everything I could. I initially didn't use the attrs in styled components but after googling for hours I think I need to use it but not sure. What am I missing?
Here is the Button component:
const Button: FunctionComponent<FullProps> = ({
as,
children,
skin = "primary",
size = "medium",
...props,
}) => {
return (
<Component
as={as}
size={size}
skin={skin}
{...props}
>
{children}
</Component>
);
};
Here is the type FullProps which has all of the props but I'm trying reduce it to the smallest issue:
export type FullProps = {
as?: ComponentType | string;
isFullWidth?: boolean;
disabled?: boolean;
shadow?: ShadowStep;
size?: Size;
skin?: Skin;
theme?: Theme;
type?: HtmlButtonType;
href?: string;
onClick?: () => void;
children?: ReactNode;
id?: string;
loadingConfig?: LoadingConfig;
icon?: IconConfig;
};
I know when using styled components you should use the prop forwardedAs to pass a as value down. That part works if I just have a simple component that takes as:
const DemoALink = styled(Button)`
color: white;
background: #fb6058;
height: 4rem;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
`;
Here is the styled component being used:
<DemoALink forwardedAs="a" skin="primary">
Testings
</DemoALink>
And this is the styling for the component:
export const Button = styled.button.attrs<FullProps>(
({ disabled, as, type }: FullProps) => ({
type: as === "button" && type ? type : undefined,
disabled,
})
// )<Required<FullProps> & { children: ReactNode }>`
)<Required<FullProps> & { children: ReactNode }>`
${baseStyles};
${({ skin, theme }) => getVariant({ skin, theme })}
padding-top: ${getHeight};
padding-bottom: ${getHeight};
box-shadow: ${shadow};
width: ${({ isFullWidth }: { isFullWidth: boolean }) =>
isFullWidth ? "100%" : "auto"};
`;
I recently ran into this same error while trying to use typescript in styled components. I eventually resolved it and found out the reason I was having that error. I'll sight an example for context.
imagine declaring a type for an avatar component like so:
interface Avatar {
src?: string,
alt: string,
height: string,
width: string,
}
It would be safe to make use of this type declaration in the desired component like so:
const AvatarContainer: FC<Avatar> = ({src, alt, width, height}) => {
return (
<Container width={width} height={height}>
<img src={src} alt={alt} />
</Container>
)
};
export default AvatarContainer;
const Container = styled.div<{width: string, height: string}>`
width: ${(props) => props.width || '35px'};
height: ${(props) => props.height || '35px'};
overflow: hidden;
border-radius: 50%;
`;
Note that the above will work correctly without erroring out. However, to reproduce the
no overload matches this call
error, let's modify the above jsx to:
const AvatarContainer: FC<Avatar> = ({src, alt, width, height}) => {
return (
<Container width={width} height={height}>
<img src={src} alt={alt} />
</Container>
)
};
export default AvatarContainer;
const Container = styled.div<Avatar>`
width: ${(props) => props.width || '35px'};
height: ${(props) => props.height || '35px'};
overflow: hidden;
border-radius: 50%;
`;
The above will error out. This is because the type definition 'Avatar' contains two more props, src and alt that our Container component does not need. What we want to do instead, is specify explicitly the props that our Container component needs as outlined in our first code example.
I hope this was helpful.
Here is how I resolved a No overload matches this call error in my styled-component:
interface Props{
someProp: string
}
const SomeStyledComponent = styled.div<{ someProp: string}>`
some-css-property: ${(props) => props.someProp}
`
export default function SomeCompnent({ someProp }: Props): ReactElement{
return(
<SomeStyledComponent someProp={someProp} />
)
}

className is missing for styled component

I have defined such "Logo" component:
const Logo = ({ className }: { className: string }) => (
<Link to="/" className={className}>
<img src={logo} alt="logo" />
</Link>
);
Which has the open possibility to be styled with passing down className.
But when I'm using it with the typescript like that:
const StyledLogo = styled(Logo)`
margin-left: 4.5rem;
`;
... somewhere in render:
<StyledLogo />
Then I've got such error:
Error:(33, 6) TS2741: Property 'className' is missing in type '{}' but
required in type 'Pick &
Partial>, "className">'.
How to let know typescript that all my styled components will pass down this className property and it will be always there?
It should be possible to resolve this issue by marking the className parameter of Logo as optional by doing the following:
/* Add ? after className */
const Logo = ({ className }: { className?: string }) => (
<Link to="/" className={className}>
<img src={logo} alt="logo" />
</Link>
);
const StyledLogo = styled(Logo)`
margin-left: 4.5rem;
`;
/* No ts error reported now */
<StyledLogo />
Hope that helps!
Update
Alternatively, to avoid the optional className method above you can take this approach:
/* Define type to minimise verbosity */
type LogoProps = { className: string };
/* Define strongly typed Logo function with defined type */
const Logo = (props: LogoProps) => (
<Link to="/" className={props.className}>
<img src={"logo"} alt="logo" />
</Link>
);
/* TS explicitly aware of prop set being passed to Logo via type */
export const StyledLogo = styled((props: LogoProps) => <Logo {...props} />)`
margin-left: 4.5rem;
`;
/* Usage */
<StyledLogo />
This method ensures that TS is aware of the actual prop set being passed to the Logo component.

React — Passing props with styled-components

I just read in the styled-components documentation that the following is wrong and it will affect render times. If that is the case, how can I refactor the code and use the required props to create a dynamic style?
Thank you in advance.
Tab component
import React from 'react'
import styled from 'styled-components'
const Tab = ({ onClick, isSelected, children }) => {
const TabWrapper = styled.li`
display: flex;
align-items: center;
justify-content: center;
padding: 100px;
margin: 1px;
font-size: 3em;
color: ${props => (isSelected ? `white` : `black`)};
background-color: ${props => (isSelected ? `black` : `#C4C4C4`)};
cursor: ${props => (isSelected ? 'default' : `pointer`)};
`
return <TabWrapper onClick={onClick}>{children}</TabWrapper>
}
export default Tab
I believe what the documentation is saying is that you should avoid including your styles inside of the rendering component:
DO THIS
const StyledWrapper = styled.div`
/* ... */
`
const Wrapper = ({ message }) => {
return <StyledWrapper>{message}</StyledWrapper>
}
INSTEAD OF THIS
const Wrapper = ({ message }) => {
// WARNING: THIS IS VERY VERY BAD AND SLOW, DO NOT DO THIS!!!
const StyledWrapper = styled.div`
/* ... */
`
return <StyledWrapper>{message}</StyledWrapper>
}
Because what happens is when the component's Props changes, then the component will re-render and the style will regenerate. Therefore it makes sense to keep it separate.
So if you read further on to the Adapting based on props section, they explain this:
const Button = styled.button`
/* Adapt the colours based on primary prop */
background: ${props => props.primary ? "palevioletred" : "white"};
color: ${props => props.primary ? "white" : "palevioletred"};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
// class X extends React.Component {
// ...
render(
<div>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
);
// }
this works because when you use the Button component in class X, it will know the props of class X without you having to tell it anything.
For your scenario, I imagine the solution would be simply:
const TabWrapper = styled.li`
display: flex;
align-items: center;
justify-content: center;
padding: 100px;
margin: 1px;
font-size: 3em;
color: ${props => (props.isSelected ? `white` : `black`)};
background-color: ${props => (props.isSelected ? `black` : `#C4C4C4`)};
cursor: ${props => (props.isSelected ? 'default' : `pointer`)};
`;
const Tab = ({ onClick, isSelected, children }) => {
return <TabWrapper onClick={onClick}>{children}</TabWrapper>
}
const X = <Tab onClick={() => console.log('clicked')} isSelected>Some Children</Tab>
I haven't tested this at all, so please feel free to try it out and let me know if it works for you or whatever worked for you!
You can pass an argument with Typescript as follows:
<StyledPaper open={open} />
...
const StyledPaper = styled(Paper)<{ open: boolean }>`
top: ${p => (p.open ? 0 : 100)}%;
`;
Another way to do it would be
const StyledDiv = styled.div.attrs((props: {color: string}) => props)`
width: 100%;
height: 100%;
background-color: ${(props) => props.color};
`
//...
render() {
return (
<StyledDiv color="black">content...</StyledDiv>
);
}
This way you are type-safe in terms of the props you want to send into the styled component. (Good when coding in Typescript)
For a more simple example with functional components:
Suppose you have an arrow like polygon and you need 2 of them pointing in different directions. So you can pass the rotate value by props
<Arrow rotates='none'/>
<Arrow rotates='180deg'/>
Then in the Component Arrow you have to pass the props like normal component to the styled component but in the styled component you have to use it like props:
import React from 'react';
import styled from "#emotion/styled";
const ArrowStyled = styled.div`
background-color: rgba(255,255,255,0.9);
width: 24px;
height: 30px;
clip-path: polygon(56% 40%,40% 50%,55% 63%,55% 93%,0% 50%,56% 9%);
transform: rotate(${props => props.rotates});
`
const Arrow = ({rotates}) => {
return (
<ArrowStyled rotates={rotates}/>
);
}
export default Arrow;
If you're using Typescript create an interface inside your styles file!
Otherwise, you won't be able to access props in your CSS
import styled from 'styled-components'
interface StyledLiProps{
selected: boolean
}
export const TabWrapper = styled.li`
// styles ...
color: ${props => (selected ? `white` : `black`)};
background-color: ${props => (selected ? `black` : `#C4C4C4`)};
`
And don`t forget to declare the props you want to use in your CSS inside your JSX
interface TabProps{
text: string;
}
const Tab = ({ text }: TabProps) => {
//...
return <TabWrapper selected={isSelected} onClick={() => updateTab}>{text}</TabWrapper>
}
Consider styled components documentation gives example of using reacts context api [2] for different themes.
[1] https://www.styled-components.com/docs/advanced
[2] https://reactjs.org/docs/context.html
Exporting styled-component
Button
and passing scrollPosition as a prop in functional component
PassingPropsToSyledComponent
import styledComponents from "styled-components";
export const Button = styledComponents.div`
position: ${ props => props.scrollPosition ? 'relative' : 'static' };
`;
export const PassingPropsToSyledComponent = ()=> {
return(
<Button scrollPosition={scrollPosition}>
Your Text Here
</Button>
)
}

React - Prop is undefined in stateless function

I am passing props from one stateless function to another, and I get a reference error saying the prop is undefined. Here is the parent function:
const Home = () => {
return (
<App>
<BackgroundImage url="mercedes-car.jpg">
<h1>Test</h1>
</BackgroundImage>
</App>
)
}
And here is the BackgroundImage function:
const Image = styled.div`
background-image: ${props => url(props.imageUrl)};
background-size: cover;
height: 100%;
width: 100%;
`;
const BackgroundImage = (props) => {
console.log(props)
return (
<Image imageUrl={ props.url }>
{ props.children }
</Image>
)
}
The error is that url is undefined; however, when I console.log(props), I get an object with url and children. Any direction or explanation as to why this error is throwing would be appreciated!
I'm guessing you meant
background-image: ${props => url(props.imageUrl)};
to be
background-image: url(${props => props.imageUrl});
since the result of that function needs to be a string. Otherwise you're trying to call a function called url.
you have a scope issue. change it to:
const BackgroundImage = (props) => {
console.log(props)
const Image = styled.div`
background-image: url(${props.url});
background-size: cover;
height: 100%;
width: 100%;
`;
return (
<Image>
{ props.children }
</Image>
)
}
basically the props are not available to your image, because styled.div is not a normal react component that has props.
Another way is to leave your code as is but set the background image from inside the return function:(and remove it from the styled.div)
return (
<Image style={{backgroundImage: `url(${props.url})`}}>
{ props.children }
</Image>
)
}

Categories