I'm using emotion styled components. I have a component called Layered which I define as follows:
import styled from "#emotion/styled";
interface LayeredProps {
[key: string]: any
}
const Layered = (props: LayeredProps) => {
return (
<ComponentWrapper style={props.style}>
<p>Hello</p>
<p>World</p>
</ComponentWrapper>
)
};
export default Layered;
const ComponentWrapper = styled.div`
display: flex;
flex-direction: column;
`
In my main code, I create another component called <LayeredModified /> which is supposed to be Layered with some extra styling. The styling is:
const LayeredModified = styled(Layered)`
margin-left: 20px !important;
margin-right: 20px !important;
`
Layered itself is never explicitly rendered, only LayeredModified. However, when it renders, it does so without any margin adjustments. In other words, only the props defined within the Layered component are shown, and not those defined in LayeredModified. I tried printing props.style in Layered and it's always undefined.
I know I'm not passing an explicit style parameter, but I thought the point of Emotion Styles was that it passed whatever styles you defined for that component automatically. If explicit passing is needed, I'm not sure how to do so without completely redefining the style inline, which defeats the purpose of the const LayeredModified component.
What am I missing?
You're missing applying the passed styles to the component. You need to add a className prop to ComponentWrapper and then use the props.className in it to apply the passed styles.
interface LayeredProps {
className?: string,
[key: string]: any
}
const Layered = (props : LayeredProps) => {
return (
<ComponentWrapper className={`${props.className}`}>
<p>Hello</p>
<p>World</p>
</ComponentWrapper>
);
};
Related
I'm learning how to use higher order components. There I want to highlight some text. In my code I can highlight the whole line by using <div>. The problem is I only want to highlight a part of the text. So I tried <span>. But when I use span the whole highlighting part doesn't work. Since it doesn't give any error I can't understand what where the error comes from.
HighlightedText.js
import React, { Component } from 'react';
import UpdatedComponent from './Hoc';
class HighlightedText extends Component {
render() {
return <h1>Highlighted Text</h1>
}
}
export default UpdatedComponent(HighlightedText);
Hoc.js
const UpdatedComponent = OriginalComponent => {
class NewComponent extends React.Component {
render() {
return(props) =>(
<div style={{ background: 'Yellow', padding: 2 }}>
<OriginalComponent {...props}/>
</div>
)
}
}
return NewComponent;
}
export default UpdatedComponent;
Issues
Your HOC looks to be trying to return a functional component from the render method of a class-based component.
Props aren't spread correctly.
padding: 2 may not be valid, it should probably provide a unit, whatever you need
Solution
To fix the highlighting I believe you just need to specify a display: inline-block; CSS rule to the div. Spread this.props from the class-based component to the wrapped component.
const updatedComponent = (OriginalComponent) => {
class NewComponent extends React.Component {
render() {
return (
<div
style={{
background: "Yellow",
padding: "1rem", // <-- provide unit, 1rem ~ 16px
display: "inline-block" // <-- inline-block display
}}
>
<OriginalComponent {...this.props} />
</div>
);
}
}
return NewComponent;
};
If you want to highlight just a part of the text, it requires modifying the virtual DOM tree returned by the original component, since you can't just easily wrap it like in your example. You might want to use react-string-replace to achieve this.
I'm trying to wrap a component with a styled-component div and apply some styles. However, for some reason, the styles aren't being applied, even though the ostensibly correct wrapper that is expected to be rendered is being rendred.
CodeSandbox
What am I doing wrong here?
You just forgot to pass the className props, see here:
const Component = ({ className }) => (
<div className={className}>Hello World!</div>
);
https://codesandbox.io/s/optimistic-lumiere-cq8gt?file=/src/App.js
I hope I helped you. Have a good day.
EDIT: Check the docs here
You need to container Component with styled.{tag} or styled(Component)
And then use that Container component to wrap main component.
import React from "react";
import "./styles.css";
import styled from "styled-components";
export default () => <Component />;
const Component = () => <StyledDiv>Hello World!</StyledDiv>;
const StyledDiv = styled.div`
font-size: 10rem;
color: red;
display: flex;
justify-content: center;
align-items: center;
`;
Check here: https://codesandbox.io/s/dank-hill-h8rbc?file=/src/App.js:128-189
Components sometimes need to be able to render as a different HTML element, depending on its context. For example, this Card component.
import React from 'react';
export type Props = {
className?: string,
renderAs: any
}
export const Card: React.FC<Props> = function Card({ renderAs, className, children }) {
const Element = renderAs;
return (
<Element className={`card ${ className ? className:'' }`}>
{ children }
</Element>
)
}
When actually using the component, all works as it is supposed to:
<Card renderAs="section">...</Card>
<Card renderAs="div">...</Card>
But I am not sure, if using renderAs: any is the correct type, for this use case and I would appreciate some insight on this!
Thank you in advance!
The actual type you are passing is React.ElementType, leaving
export type Props = {
className?: string,
renderAs: React.ElementType
}
What you say by this is that you want to enable renderAs to be set to some intrinsic JSX element such as section, div, span ...
I would like an HOC generated by styled-components to re-render when one of its properties get changed. I'm using MobX for change detection.
This doesn't respond to changes, I think I understand why. The question is if there is a simple workaround to make it work.
const DemoComponent = observer(styled.div`
background-color: ${props => props.myObject.myObservableIsTrue ? 'red' :
'green'};
`);
It's hard to tell by this little snippet, but one of my guesses would be you are not injecting any store, so currently, no store is being connected to your component.
here's a simple example of how I used styled-components with mobx if it helps:
EDITED:
I've updated the code example.
Do you know the Container / Presentational pattern?
This was the missing link.
In order to keep your renders as little as possible
you need to separate your stateful component from each other.
Spread them across a Container component (aka Dumb Components)
This way you separate state concerns and render only the component with the changed state.
UPDATED:
https://codesandbox.io/s/zxx6o2pq3l
Sorry!!! A bit of a hack job, but attempt to bring your entire code inside the #inject("store") class:
import React from "react";
import { observer, inject } from "mobx-react";
import styled, { css } from "styled-components";
#inject("store")
#observer
export default class OtherComponent extends React.Component {
render() {
const MyWrapper = (store) => {
const Wrapper = styled.div`
border: 1px solid black;
display: flex;
justify-content: space-between;
color: ${({ color }) => color};
border: 2px solid ${({ color }) => color || "black"};
padding: 10px;
margin-bottom: 10px;
`;
return (
<Wrapper {...store}>
styled-component
<button onClick={store.changeColor}>change color</button>
</Wrapper>
);
}
const { store } = this.props;
return (
<div>
{
MyWrapper(store)
}
</div>
);
}
}
Mobx is actually read like this: #inject("store") #observer export default class...
So it really an extension of an extended component; only wrapped variables will apply!
I had trouble naming this question and it seems quite broad, so, forgive me oh moderators. I'm trying out styled components for the first time and trying to integrate it into my react app. I have the following so far:
import React from 'react';
import styled from 'styled-components';
const Heading = styled.h1`
background: red;
`;
class Heading extends React.Component {
render () {
return (
<Heading>
{this.props.title}
</Heading>
);
}
}
export default Heading;
So, just a normal class, but then I import styled components up top, define the const Heading, where I specify that a Heading really is just a styled h1. But I get an error stating that Heading is a duplicate declaration since I also say class Heading....
I'm obviously completely missing something here. All the examples online doesn't actually show how you also use this with React. I.e. where do I define my class, my constructor, set my state, etc.
Do I have to move the styled component into it's own file, i.e.:
import styled from 'styled-components';
const Heading = styled.h1`
background: red;
`;
export default Heading;
Then create a React component that will serve as a wrapper of sorts, e.g. 'HeadingWrapper':
import React from 'react';
import Heading from './Heading';
class HeadingWrapper extends React.Component {
render () {
return (
<Heading>
{this.props.title}
</Heading>
);
}
}
export default HeadingWrapper;
A bit of clarity on this would greatly be appreciated! Thanks :)
styled.h1`...` (for example) returns a React component that works just like <h1>. In other words, you use <h1> like this:
<h1>h1's children</h1>
...so when you do const Heading = styled.h1`...`;, you'll use <Heading> the same way:
<Heading>Heading's children</Heading>
If you want a component that behaves differently, e.g. one that uses the title prop instead of children, you'll need to define such a component, and it will need to have a different name than the Heading component you already defined.
For example:
const styled = window.styled.default;
const Heading = styled.h1`
background: red;
`;
const TitleHeading = ({title}) => <Heading>{title}</Heading>;
// ...or...
class StatefulTitleHeading extends React.Component {
render() {
return <Heading>{this.props.title}</Heading>;
}
}
ReactDOM.render(
<div>
<Heading>I'm Heading</Heading>
<TitleHeading title="I'm TitleHeading"/>
<StatefulTitleHeading title="I'm StatefulTitleHeading"/>
</div>,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://unpkg.com/styled-components#1.4.3/dist/styled-components.js"></script>
<div id="container"></div>
Frankly, though, it makes more sense to just use the component returend by styled.h1 directly:
const Heading = styled.h1`...`;
export default Heading;
// ...then...
<Heading>Children go here</Heading>
The semantics of children are already clear, and using <Heading title="Children go here"/> instead detracts significantly from that.
Let me make this really simple for you.
Let's create one styled component for heading named 'Heading'
const Heading = styled.h1`
color: 'black';
font-size: 2rem;
`
and now you can use it like following.
<Heading>{this.props.title}</Heading>
How you manage to get the title prop as it's child is no concern of style component's. It only manages how that title looks. Styled component is not a container that maintains your app/business logic.
Now let's look at an example in it's entirety.
import styled from 'styled-components'
// Heading.js (Your styled component)
const Heading = styled.h1`
color: 'black';
font-size: 2rem;
`
export default Heading
and now your container that will use your styled component
// Header.jsx (Your container)
class Header extends Component {
componentWillReceiveProps(nextProps) {
// This your title that you receive as props
console.log(nextProps.title)
}
render() {
const { title } = this.props
return (
<div id="wrapper">
<Heading>{title}</Heading>
</div>
)
}
}
I hope that helps. Let me know if you need further clarification.