I have a weird problem with stlyed components. I have a component Header with a basic style but when a try to use this component and extend the style nothing happens. Can someone tell me what going on?
import styled from 'styled-components/native';
export const Container = styled.SafeAreaView``;
export const Content = styled.View`
height: 72px;
padding: 0 24px;
flex-direction: row;
align-items: center;
justify-content: center;
`;
Header component
import React, { PropsWithChildren, FC } from 'react';
import { Container, Content } from './styles';
const Header: FC = ({ children }: PropsWithChildren<unknown>, props) => {
return (
<Container {...props}>
<Content>{children}</Content>
</Container>
);
};
export default Header;
import styled from 'styled-components/native';
import Header from '../components/Header/index';
export const Container = styled(Header)`
background: blue;
height: 200px;
`;
You have to pass your props from into your Header component. In Container or Content. It's won't be done instead of you.
Your Header is a React component and he "doesn't know what to do" with props that it will receive from Container - const Container = styled(Header)'...'.
Props will be recognized correctly if component is working with styles, as Text, View, ...
export const Container = styled(Header)`
background: blue;
height: 200px;
`;
const Header: FC = ({ children, ...restProps }: PropsWithChildren<unknown>) => {
return (
<Container {...restProps}>
<Content>{children}</Content> // or <Content {...restProps}>...
</Container>
);
};
or you have 2 next options, without passing the props - just editing your inner Container. It's depends on your codestyle of the project
const Header: FC = ({ children }: PropsWithChildren<unknown>) => {
return (
<Container background="blue" height="200px">
<Content>{children}</Content>
</Container>
);
};
export const NewContainer = styled(Container)`
background: blue;
height: 200px;
`;
const Header: FC = ({ children }: PropsWithChildren<unknown>) => {
return (
<NewContainer>
<Content>{children}</Content>
</NewContainer>
);
};
Related
I have a react component that looks like this:
import { TextareaHTMLAttributes} from 'react'
import styled from 'styled-components'
const TextAreaElement = styled.textarea`
border-radius: 40px;
border: none;
background: white;
`
const TextArea = (props: TextareaHTMLAttributes<any>) => { <--- replace <any> here
return <TextAreaElement {...props} />
}
I know I can do something like this, but would rather not have to add every prop manually:
const TextArea = ({placeholder} : {placeholder: string}) => {
return <TextAreaElement placeholder={placeholder} />
}
You can pass the props as regular HTML element
import React from "react";
const CustomTA = (props: React.HTMLProps<HTMLTextAreaElement>) => {
return <textarea {...props} />;
};
I am new to Reactjs and building a sample e commerce application in Reactjs. I am using single context API to share multiple filter values across application. When I update values from any of the filter components, all the filter components get re rendered again. Is there any way to restrict this behavior as it causes performance issues. Sharing the sample code link below.
stackblitz
App component
import logo from './logo.svg';
import React, { useState } from 'react';
import './App.css';
import Categories from './components/Categories/Categories';
import Filters from './components/Filters/Filters';
import Products from './components/Products/Products';
import styled from 'styled-components';
import { FilterContext } from './components/Contexts/FilterContext';
const MainContainer = styled.div`
display: flex;
padding: 20px;
`
function App() {
console.log('App component');
const [rangeval, setRangeval] = useState(null);
const [colorCodes, setcolorCodes] = useState([]);
return (
<div className="App">
<Categories/>
<FilterContext.Provider value={React.useMemo(()=>({slider: {rangeval, setRangeval}, color: {colorCodes, setcolorCodes} }), rangeval, setRangeval, colorCodes, setcolorCodes)} >
<MainContainer>
<Filters/>
<Products />
</MainContainer>
</FilterContext.Provider>
</div>
);
}
export default React.memo(App);
Below are my filter components.
Color component
import axios from 'axios';
import React, { useCallback, useContext, useEffect, useState } from 'react';
import styled from 'styled-components';
import { FilterContext } from '../../Contexts/FilterContext';
const ColorWrapper = styled.div`
display: flex;
flex-direction: column;
padding: 20px 10px 20px 10px;
border-bottom: 1px solid grey;
`;
function Color() {
console.log('Color Component');
const { color } = useContext(FilterContext);
const [colors, setColors] = useState([]);
const colorCodes = [];
const handleCheckboxState = (data) => {
if (data.isChecked) {
colorCodes = colorCodes.filter((item) => item != data.id);
} else {
colorCodes.push(data.id);
}
colors.find((item) => item.id === data.id).isChecked = !data.isChecked;
setColors(colors);
color.setcolorCodes(colorCodes);
};
useEffect(() => {
axios
.get('https://run.mocky.io/v3/fdbe3884-b824-466c-8da4-0d0ecad17e7c')
.then((response) => {
setColors(response.data);
})
.catch((error) => {})
.finally();
}, []);
return (
<ColorWrapper>
<div>Color</div>
{colors.map((color) => (
<label key={color.id}>
<input
type="checkbox"
checked={color.isChecked}
name="color"
value={color.id}
onChange={() => handleCheckboxState(color)}
/>
{color.name}
</label>
))}
</ColorWrapper>
);
}
export default React.memo(Color);
Slider component
import React, { useContext, useState } from 'react';
import { FilterContext } from '../../Contexts/FilterContext';
import styled from 'styled-components';
const SliderWrapper = styled.div`
display: flex;
flex-direction: column;
padding: 20px 10px 20px 10px;
border-bottom: 1px solid grey;
`;
const SliderInput = styled.input`
width: 100%;
`;
const SliderRange = styled.div`
margin: 20px 0px 20px 0px;
`;
function Slider() {
console.log('Slider component');
const { slider } = useContext(FilterContext);
const [min, setMin] = useState(10);
const [max, setMax] = useState(10000);
return (
<SliderWrapper>
<SliderRange>
Price Range 0 - {slider.rangeval ? slider.rangeval : min}
</SliderRange>
<SliderInput
type="range"
className="range"
min={min}
max={max}
onChange={(event) => slider.setRangeval(event.target.value)}
/>
</SliderWrapper>
);
}
export default React.memo(Slider);
I am trying to create a reusable component where I have redux-form <Field /> returned and in this component I am styling it with styled-component.
the challenge I have is that none of the style is reflecting
this is my simple-field-input.styles.ts
import React from 'react';
import { Field } from 'redux-form';
import styled from 'styled-components';
import { SimpleFieldProps } from './simple-field-input.type';
const ReduxFormField: React.FC<SimpleFieldProps> = ({ componentType }) => {
return <Field component={componentType} name="email" />;
};
export const Container = styled(ReduxFormField)`
outline: none;
border: none;
background-color: orangered;
color: yellow;
`;
and here is my simple-field-input.component.tsx
import React from 'react';
import * as Style from '../simple-field-input/simple-field-input.styles';
import { SimpleFieldProps } from './simple-field-input.type';
const FieldInput: React.FC<SimpleFieldProps> = ({ componentType }) => {
return <Style.Container componentType={componentType}></Style.Container>;
};
export default FieldInput;
it simple renders the input but not implementing the styles...
i will appreciate any help. thanks
When using styled on on component which is not a DOM element, what it does is add a className prop to the component. The component needs to access that className and pass it through to a DOM element.
You're actually passing it down twice here, since the className should end up on the componentType rather than the Field itself.
const ReduxFormField: React.FC<SimpleFieldProps & {className?: string}> = (
{ componentType, className }
) => {
return <Field component={componentType} props={{className}} name="email" />;
};
For styled-components, how can I fetch the height of any particular div?
For e.g.., in the below example, how can I get the height(100px) of Outer tag,
import React from "react";
import ReactDOM from "react-dom";
import styled from "styled-components";
import "./styles.css";
function App() {
const Outer = styled.div`
height: "100px";
border: 1px solid black;
`;
return (
<div className="App">
<Outer>Inside</Outer>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I have tried Outer.height() but it doesn't work. How can I get the height of Outer tag?
You can add reference to get size of Outer and do this.outerRef.current.clientHeight
Something like that :
const Outer = styled.div`
height: 100px;
background: red;
`;
export default class App extends React.Component {
constructor(props) {
super(props);
this.outerRef = React.createRef();
this.state = {
outerHeight: 0
};
}
componentDidMount() {
this.setState({ outerHeight: this.outerRef.current.clientHeight });
}
render() {
return (
<Container ref={this.outerRef}>
{this.state.outerHeight}
</Container>
);
}
}
You can check on : https://codesandbox.io/embed/6yq239ljlk?fontsize=14
This works but I don't know if it's on this way you need height.
You can declare outer tag and use on tag.
function App() {
const heightApp: 100
const Outer = styled.div`
height: "`${heightApp}`px";
border: 1px solid black;
`;
return (
<div className="App">
<Outer>Inside</Outer>
</div>
);
}
How can I access the ThemeProvider props in global.js when using styled-components?
For example in theme.js I have ${props => props.theme.fonts.fontSize} calling a default font size of 16px
const theme = {
fonts: {
fontSize : '16px',
}
}
export default theme
This is provided in /layouts/index.js as
import React from 'react'
import { ThemeProvider } from 'styled-components'
import '../style/global';
import theme from '../style/theme'
class Template extends React.Component {
render() {
const { children } = this.props
return (
<ThemeProvider theme={theme}>
...
{children()}
...
</ThemeProvider>
)
}
}
export default Template
From here I can access the ${props => props.theme.fonts.fontSize} within each component or child page.
But how can I pass to global.js in the same way when global is technically a level above theme.js? So that I could create a global style as
injectGlobal`
html {
font-size: (${props => props.theme.fonts.fontSize} / 16px) * 1em;
}
`
The easiest way off solving this is by creating a top level component that injects your desired styling like this:
import { Children } from 'react';
import { withTheme, injectGlobal } from 'styled-components';
const GlobalComponent = ({ theme, children }) => {
injectGlobal`
font-size: ${theme.fonts.fontSize}
}
`;
return Children.only(children);
};
export default withTheme(Global);
This will make sure all Components that have this Component as a parent will have the desired globalStyling. Hope this helped
Late but now we can actually create a Global Component and pass it as a child of ThemeProvider. It will allow you to access all the props of current theme.
Example for applying font family:
Your Global.js / Global.ts
import { createGlobalStyle } from "styled-components";
const GlobalStyle = createGlobalStyle`
html,
body {
padding: 0;
margin: 0;
font-family: ${(props) => props.theme.font.family}
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
`;
export default GlobalStyle;
Your Main component app.tsx / app.jsx
import theme...
import { ThemeProvider } ...
imort GlobalStyle from '../path-to-global-file';
const App ...
.
.
return(
<>
<ThemeProvider theme={theme}>
<GlobalStyle />
{ /* Root component */ }
<Component/>
</ThemeProvider>
</>
);
You can use the props easily now.