Add srcSet to styled-component themed image - reactjs

I have multiple themes for my website. I'm attempting to add a srcSet to an image like this:
export const Header = styled.img.attrs({
src: props => props.theme.images.Header1x,
srcSet: `${props => props.theme.images.Header1x} 1x, ${props => props.theme.images.Header2x} 2x, ${props => props.theme.images.Header3x} 3x`,
})`
height: 42px
`;
The src attribute is proper, but srcSet is not:
<img src="/images/header.jpg" srcset="function (props) {
return props.theme.images.Header1x;
} 1x, function (props) {
return props.theme.images.Header2x;
} 2x, function (props) {
return props.theme.images.Header3x;
} 3x" class="sc-VJcYb epAqoN">
styled-components theming feature is wonderful, and I'd like to keep all my theming organized with it, but I can't think of a way to properly build a srcSet with it. Anyone have any strategies, or do I need to go in another direction entirely?

The value of your srcSet property is a template string but should be a function that returns a string.
export const Header = styled.img.attrs({
src: props => props.theme.images.Header1x,
srcSet: props => `${props.theme.images.Header1x} 1x, ${props.theme.images.Header2x} 2x, ${props.theme.images.Header3x} 3x`,
})`
height: 42px
`;

Related

How to properly use TypeScript types with a Styled Component canvas on React

I am creating a React component on Gatsby using TypeScript and have defined a canvas Styled Component this way:
const Background = styled.canvas`
position: fixed;
width: 100%;
height: 100%;
`;
And to use it I am assigning types to it this way:
const canvas: HTMLCanvasElement = Background,
context = Background.getContext('2d');
But I am getting this error with the canvas type:
Type 'String & StyledComponentBase<"canvas", any, {}, never> & NonReactStatics<never, {}>' is missing the following properties from type 'HTMLCanvasElement': height, width, getContext, toBlob, and 238 more.
I am also getting an error on the .getContext() method:
This expression is not callable.
Type 'never' has no call signatures.
I have been searching for a solution but can not find a proper one for this specific problem.
Could someone please explain to me the best way to use Styled Component canvas with TypeScript?
The answer from #101arrowz was almost right but had a problem with the context that gave me this error.
Object is possibly 'null'.
But it helped me to find a different approach with the styles being inline and solve the problem this way:
const Component: React.FC = () => {
const canvasRef = React.useRef<HTMLCanvasElement>(null);
const [context, setContext] = React.useState<CanvasRenderingContext2D | null>(
null
);
React.useEffect(() => {
if (canvasRef.current) {
const renderCtx = canvasRef.current.getContext('2d');
if (renderCtx) {
setContext(renderCtx);
}
}
}, [context]);
return (
<div>
<canvas
id="canvas"
ref={canvasRef}
style={{
position: 'fixed',
width: '100%',
height: '100%'
}}
></canvas>
</div>
);
The Background is a React.Component (i.e. a function that creates a virtual element) rather than an HTMLCanvasElement. Not only does the function need to be called for it to even return anything remotely like an HTMLCanvasElement, but you also need access to the underlying DOM element to make it work. I do have a suggestion that you might be able to try, though.
import { useRef, useEffect } from 'react';
const Background = styled.canvas`
position: fixed;
width: 100%;
height: 100%;
`;
const ComponentUsingTheCanvas = () => {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const context = canvasRef.current.getContext('2d');
// Do stuff with your canvas context here
});
return (
<Background ref={canvasRef} />
);
}
One note, I didn't type anything because TypeScript can do that automatically most of the time.
By the way, why use a styled component when you can just style inline? styled-components is only really useful if you do something with the props.

Styled Components - content passed through props (content)

I am trying to toggle text inside a div (styled component) depending on the props value. The idea is simple: state changes, its value goes to props of the styled component, and then depending what it is, the content adjusts. What is not working is the content in before / after. Any idea why?
STYLED COMPONENT:
export const Info = styled.div`
padding: 5px;
margin-top: 15px;
&::after {
content: ${props => props.content === "intro" && "hello"};
}
`
JS
const CheckNumber = () => {
const [msg, setMsg] = useState("intro")
return (
<Info content={msg}/>
)
}
export default CheckNumber
Your current code generates style
content: hello
that is invalid CSS. You need extra quotes(one pair for JS level and nested quotes for CSS):
&::after {
content: ${props => props.content === "intro" && "'hello'"};
}
PS found that really accidentally by duplicating content like:
&::after {
content: "aaaa";
content: ${props => props.content === "intro" && "hello"};
}
because browser just did not create "::after" prseudoelement while content is invalid - so we were unable to check what styles were actually generated.

Arbitrarily injecting styles with styled-components

I'm populating a grid with various controls (in this example: up-down counter and a text box).
Currently, I'm injecting styles in the cls member (in this example can be e.g. wide-input and narrow-input):
render(): ReactNode {
const input: CellItem[] = [
{ isUpdown: false, cls: 'wide-input' },
{ isUpdown: true, cls: 'narrow-input' },
];
return (
<GridContainer>
input.map(content, index): ReactNode => {
return (
content.isUpdown ?
<StyledUpdownCell className={content.cls} /> :
<StyledTextBoxCell className={content.cls} /> :
)
}
</GridContainer>
);
}
My question is what is the proper way to do it using styled-components?
Is there a way to inject any arbitrary style (content.cls in this example, but tomorrow it could be also setting custom border color for instance)
Using styled components, you can have access to props passed to your custom styled component.
So, you could create different 'themes' props which you pass to your StyledUpdownCell and then access those inside the component styles. For example
const StyledUpdownCell = styled.div`
border-color: ${props => props.warningTheme ? 'red' : 'black'};
`
in use:
<StyledUpdownCell warningTheme />
You could also pass props directly but with a default e.g.
const StyledUpdownCell = styled.div`
border-color: ${props => props.borderColor || 'black'};
`
in use:
<StyledUpdownCell borderColor="violet" />
It's really up to you and how you want to design your component's API.
Side note: I've found this little library helpful for when creating components which have a lot of different props: https://github.com/yldio/styled-is

Creating styled-component with React.createElement

I would like to create a styled-component with a function, taking element as an argument, which I create with React.createElement call.
const StyledComponent = (element) => styled(element)`
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
`;
const myComponent = (props) => {
const element = React.createElement('div', null, `Hello World!`);
return (
StyledComponent(element)
);
}
This results in following error:
Uncaught Error: Cannot create styled-component for component: [object Object].
I am probably missing something, but am unable to figure it out.
You can't create a HOC of a rendered React node. The following line is a render call:
const element = React.createElement('div', null, `Hello World!`);
// this is the same as:
const element = <div >Hello World</div>
What you may want is to replace the createElement with an actual functional component:
const element = () => React.createElement('div', null, `Hello World!`);
// this is the same as:
const element = () => <div >Hello World</div>
Now you can create a styled component and render it. Full (relevant) code:
const element = () => React.createElement('div', null, `Hello World!`);
const SComponent = StyledComponent(element);
return React.createElement(SComponent);
Felix answer was almost right, but you still need to pass the className property in the inline component function in order to get the styles defined in your styled component applied.
I was able to find the right answer in this thread, I decided to use the JSX-syntax, If you need to pass refs, use React.cloneElement instead.
Complete example:
const StyleMyElement = (Element) => styled(({ className }) => <Element.type {...Element.props} className={className} />)`
position: absolute;
top: 0;
...
`;
...
const element = () => React.createElement('div', null, `Hello World!`);
const StyledComponent = StyleMyElement(element);
return (
<StyledComponent />
)
As of styled-components v4 you can just use the as prop to do this easily.
From the docs:
If you want to keep all the styling you've applied to a component but just switch out what's being ultimately rendered (be it a different HTML tag or a different custom component), you can use the "as" prop to do this at runtime.
import styled from "styled-components";
const Component = styled.div`
color: red;
`;
render(
<Component
as="button"
onClick={() => alert('It works!')}
>
Hello World!
</Component>
)
Here's how I implemented it:
The component:
export default function StyledCustomComponent({tag, className, children}) {
const customTag = React.createElement(tag);
const StyledCustomElement = styled(customTag.type)`
"some css rules"
`
return (
<StyledCustomElement {...className}>
{children}
</StyledCustomElement>
)
}
Then calling the component in another file:
<StyledCustomElement tag='div'> Some content <StyledCustomElement/>
Hope it helped!
Let make things simple
import styled from 'styled-component'//import library for making styled components
If for intance would like to create a styled component based on a <p></p> you would do lile this:
const MyP=styled.p
color:red;
font-size:10px;
font-weight:bold
;
You can ads as much css defintion as you want.
Now to use it:
const MyComponent=()=>{
<MyP>
Hello
</MyP>
}
Here instead of 'MyP' you could use 'p'(not styled).
It will also work for the const MyComponent=React.creatElement(MyP,null,'Hello');
Hope it helped

How to pass prop in styledcomponent to stylerule?

I am using styledcomponents in my reactjs app. In one of the components I have a mediarule:
#media (max-width: 1450px)
{
padding-right: ${props => (props.meta.count > 0 ? '6px' : '0')};
}
This meta.count prop does exist but when I run the app I get:
TypeError: Cannot read property 'count' of undefined
The render starts like this:
const { lastUpdated, meta } = this.props;
The meta prop gets loaded through a redux selector:
const mapStateToProps = state => ({
meta: getAlertsMeta(state),
});
How can I avoid this / fix this?

Resources