How to send props value to styled component in emotion react? - reactjs

I am new to MUI and react, I am trying make input textbox component to reuse them, instead of creating every time.
I am trying to set width value by passing as a props. how can I achieve it.
InputText - functional comp
import React from 'react'
import styled from '#emotion/styled'
import { Box } from '#mui/system'
type propsInputText ={
width:string;
}
const TextBox = styled.input`
width:${props => props.width? props.width : '30px;'}
`
function InputText(props: propsInputText) {
return (
<TextBox />
)
}
export default InputText
MainPage - components
import InputText from '../Tools/TextBox/InputText';
<InputText width='200px' />
Note
It is not just to set width only, I am trying pass anything to styled components ex: color,font-size,height.

I found the solution, I just forget to pass the parameter on the functional component,
type propsInputText ={
width:string;
}
const TextBox = styled.input`
width:${props => props.width? props.width : '30px;'}
`
function InputText(props: propsInputText) {
console.log("InputText Props = "+props.width);
return (
<TextBox width={props.width} /> //here I need to add the props
)
}
export default InputText

Do you want something like this?
const InputBox = (props) => {
const TextBox = styled.input(({ theme }) => ({
width: props.width ? props.width : 200,
color: props.color ? props.color : "blue",
background: props.background? props.background : "yellow",
height: props.height? props.height : 40
}));
return <TextBox />;
};
And then you could implement it like this:
<>
<InputBox width={100} color={"white"} background={"black"} height={"10"} />
<InputBox />
</>

Related

How to style a component based on state in Styled Components?

I am trying to change the input background based on the useState hook in Styled Components in React.
Here is my code:
const [searchActive, setSearchActive] = useState(true);
<div className="search" searchActive={searchActive}>
<input id="input"/>
</div>
Here is my Styled Component code:
.search {
background: ${(searchActive) => (searchActive ? "red" : "yellow")};
}
any advise would be very appreciated.
Creating a component and passing in props will work:
import React from 'react'
import styled from 'styled-components'
const Search = styled.div`
background: ${props => (props.searchActive ? 'red' : `yellow`)};
`
const Parent = () => {
return (
<Search searchActive={searchActive}>
<input id="input" />
</Search>
)
}
export default Parent
Only different is whatever you have style wise for search can be added to the Search component but you do not show any further code so I do not know how you're bringing it in.
You can also externalize the components with something like:
import styled from 'styled-components'
export const Search = styled.div`
background: ${props => (props.searchActive ? 'red' : `yellow`)};
`
then bring it in:
import {Search} from './search.js'
Add 'props' in styled-component like this;
.search {
background: ${props => props.searchActive? "red" : "yellow"};
}
You can declare and directly use Styled Components in jsx . They are not required to be used as a className. Maybe this approach is useful for you.
import React, { useState } from "react";
import styled from "styled-components";
export default function App() {
const [searchActive, setSearchActive] = useState(true);
const [value, setValue] = useState("");
const Search = styled.input`
background: ${(props) => (props.searchActive ? "red" : "yellow")};
`;
return (
<Search
searchActive={searchActive}
value={value}
onChange={(e) => {
setValue(e.target.value);
setSearchActive(false);
}}
/>
);
}

how to change image src using props with styled component and react

I want to reuse my component for multiple pages.
so, I have to change img src in styled.img component as passing props.
is it possible to change image src on styled component ?
I applied my code like below.
it's not working..
//logic
import Myimg from './img/icon_my';
const OngoingLists = ({ ...props }) => {
return (
<ListsOutWrap>
<ProgramListWrap {...props}>
<IconWrap {...props} >
<MyIcon {...props} />
</IconWrap>
</<ProgramListWrap>
</ListsOutWrap>
);
}
// styled component
const MyIcon = styled.img.attrs({
src: props => props.Img || { Myimg },
})`
width: 100%;
`;
I tried like above, any image doesn't show...even default img file doesn't show..
how can I change src file by using props ??
should I use background-image ?
thanks for ur help in advance ! :)
Styled components are still just react components, and you can simply provide a default prop value in the case a src prop isn't passed when used.
import Myimg from './img/icon_my';
const MyIcon = styled.img`
width: 100%;
`;
MyIcon.defaultProps = {
src: Myimg,
};
Usage:
<MyIcon /> // uses default Myimg
<MyIcon src="/path/to/sourceImage" /> // uses "/path/to/sourceImage"
BTW Correct attrs format to accept props would be to define a function taking props and returning an object:
const MyIcon = styled.img.attrs(props => ({
src: props.Img || Myimg,
}))`
width: 100px;
`;

Passing custom props to each styled component through Provider

I would like to pass a custom prop (exactly: theme name as string) to each passed styled component through Provider, so it was available throughout the css definition.
ThemeProvider almost does it, but it expects object, not the string. I do not want to pass whole object with theme settings, just the name of my theme.
I do not want to use special theme prop or similar, because then I would have to it manually every single time I create new styled component. Provider seems like the best option if only it cooperated with string.
Is there any possibility to pass a string through Provider to Consumer builded in styled components?
EDIT:
[PARTIAL SOLUTION]
I found what I was looking for when I realized styled-components exports their inner context. That was it. Having access to pure react context gives you original Provider, without any 'only objects' restriction ('only objects' is a styled-components custom provider restriction).
Now I can push to each styled component exactly what I want and if I want.
import styled, { ThemeContext } from 'styled-components';
const StyledComponent = styled.div`
color: ${props => props.theme == 'dark' ? 'white' : 'black'};
`;
const Component = props => {
const theme = 'dark';
return (
<ThemeContext.Provider value={theme}>
<NextLevelComponent>
<StyledComponent />
</NextLevelComponent>
</ThemeContext.Provider>
);
};
Hope I have this correct, from what I've been able to glean. I haven't tried this out but it seems it might work for you. This is lifted directly from the reactjs.org docs regarding context. It passed the string name of the theme down.
const ThemeContext = React.createContext('green');
class App extends React.Component {
render() {
return (
<ThemeContext.Provider value="blue">
<SomeComponent />
</ThemeContext.Provider>
);
}
}
function SomeComponent(props) {
return (
<div>
<OtherComponent />
</div>
);
}
class OtherComponent extends React.Component {
static contextType = ThemeContext;
render() {
return <ThirdComponent theme={this.context} />
}
}
I hope this helps you understand the idea behind ThemeContext from styled-components. I've passed string "blue" to ThemeContext just to show, that it should not be object and you can use just string.
import React, { useContext } from "react";
import ReactDOM from "react-dom";
import styled, { ThemeContext } from "styled-components";
// Define styled button
const Button = styled.button`
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border-radius: 3px;
color: ${props => props.theme};
border: 2px solid ${props => props.theme};
`;
// Define the name of the theme / color (string)
const themeName = "blue";
const ThemedButton = () => {
// Get the name from context
const themeName = useContext(ThemeContext);
return <Button theme={themeName}>Themed color: {themeName}</Button>;
};
function App() {
return (
<div className="App">
<ThemeContext.Provider value={themeName}>
<ThemedButton />
</ThemeContext.Provider>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Demo: https://codesandbox.io/s/styled-components-example-with-themecontext-cso55

react-custom-scrollbars jumps to top on any action

I am using react-custom-scrollbars in a react web app because I need to have two independant scroll bars, one for the main panel and one for the drawer panel. My issue is that the content in the main panel is dynamic and whenever I take some action in the main panel that changes state the scroll bar jumps to the top of the panel again.
UPDATE:
I believe I need to list for onUpdate and handle the scroll position there. If it has changed then update if not do not move the position?
In code, I have a HOC call withScrollbar as follows:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import AutoSizer from 'react-virtualized-auto-sizer';
import { Scrollbars } from 'react-custom-scrollbars';
import { colors } from '../../theme/vars';
import { themes } from '../../types';
// This is a Higher Order Component (HOC) used to
// provide a scroll bar to other components
export default (ChildComponent, styling) => {
class ComposedComponent extends Component {
state = {
// position: ??
};
handleUpdate = () => {
//update position
//this.scrollbar.scrollToBottom();
};
render() {
return (
<AutoSizer>
{
({ width, height }) => (
<Scrollbars
style={{ width, height, backgroundColor: colors.WHITE, overflow: 'hidden', ...styling }}
onUpdate={() => this.handleUpdate()}
renderThumbVertical={props => <Thumb {...props} />}
autoHide
autoHideTimeout={1000}
autoHideDuration={200}
>
<ChildComponent {...this.props} />
</Scrollbars>
)
}
</AutoSizer>
);
}
}
return ComposedComponent;
};
const Thumb = styled.div`
background-color: ${props =>
props.theme.theme === themes.LIGHT ? colors.BLACK : colors.WHITE};
border-radius: 4px;
`;
in my MainView component I just wrap the export like this:
export default withScrollbar(LanguageProvider(connect(mapStateToProps, null)(MainView)));
I have read a few similar issues on this like this one: How to set initial scrollTop value to and this one scrollTo event but I cannot figure out how to implement in my case. Any tips or suggestions are greatly appreciated.
So I found a way to get this to work and it feels like a complete hack but I'm posting in hopes it might help someone else.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import AutoSizer from 'react-virtualized-auto-sizer';
import { Scrollbars } from 'react-custom-scrollbars';
import { colors } from '../../theme/vars';
import { themes } from '../../types';
// This is a Higher Order Component (HOC) used to
// provide a scroll bar to other components
export default (ChildComponent, styling) => {
class ComposedComponent extends Component {
state = {
stateScrollTop: 0,
};
onScrollStart = () => {
if (this.props.childRef) { // need to pass in a ref from the child component
const { scrollTop } = this.props.childRef.current.getValues();
const deltaY = Math.abs(scrollTop - this.state.stateScrollTop);
if (deltaY > 100) { // 100 is arbitrary. It should not be a high value...
this.props.childRef.current.scrollTop(this.state.stateScrollTop);
}
}
};
handleUpdate = () => {
if (this.props.childRef) {
const { scrollTop } = this.props.childRef.current.getValues();
this.setState({ stateScrollTop: scrollTop });
}
};
render() {
return (
<AutoSizer>
{
({ width, height }) => (
<Scrollbars
ref={this.props.childRef}
style={{ width, height, backgroundColor: colors.WHITE, overflow: 'hidden', ...styling }}
onScrollStart={e => this.onScrollStart(e)}
onUpdate={e => this.handleUpdate(e)}
renderThumbVertical={props => <Thumb {...props} />}
autoHide
autoHideTimeout={1000}
autoHideDuration={200}
>
<ChildComponent {...this.props} />
</Scrollbars>
)
}
</AutoSizer>
);
}
}
return ComposedComponent;
};
const Thumb = styled.div`
background-color: ${props =>
props.theme.theme === themes.LIGHT ? colors.BLACK : colors.WHITE};
border-radius: 4px;
`;
I use this HOC like this:
create a ref for the component you want to use it with
pass the ref to the component that will use the HOC:
class SomeChildComponent extends Component {
...
viewRef = React.createRef();
...
render() {
return ( <MainView childRef={this.viewRef} />)
}
import and wrap the component
import withScrollbar from '../../hoc/withScrollbar';
...
export default withScrollbar(MainView);
I tried the above solution and it didn't seem to work for me.
However what did work was making sure that my child components inside Scrollbars were wrapped in a div with a height of 100%:
<Scrollbars>
<div style={{ height: '100%' }}>
<ChildComponent />
<ChildComponent />
</div>
</Scrollbars>

How to use the styled-component property innerRef with a React stateless component?

I have the following styled component:
const Component = styled.div`
...
`;
const Button = (props) => {
return (
<Component>
...
</Component>
);
};
export default styled(Button)``;
I want to get a reference to the underlying div of Component. When I do the following I get null:
import Button from './Button.js';
class Foo extends React.Component {
getRef = () => {
console.log(this.btn);
}
render() {
return (
<Button innerRef={elem => this.btn = elem} />
);
}
}
Any ideas why I am getting null and any suggestions on how to access the underlying div?
Note: The reason I am doing this export default styled(Button)``; is so that the export styled component can be easily extended.
I managed to accomplish this by passing a function down as a prop to the styled-component that I was targeting, then passing the ref back as an argument of the function:
const Component = styled.div`
...
`;
const Button = (props) => {
return (
<Component innerRef={elem => props.getRef(elem)}>
...
</Component>
);
};
export default styled(Button)``;
...
import Button from './Button.js';
class Foo extends React.Component {
getRef = (ref) => {
console.log(ref);
}
render() {
return (
<Button getRef={this.getRef} />
);
}
}
Passing a ref prop to a styled component will give you an instance of the StyledComponent wrapper, but not to the underlying DOM node. This is due to how refs work. So it's not possible to call DOM methods, like focus, on styled components wrappers directly.
Thus to get a ref to the actual, wrapped inner DOM node, callback is passed to the innerRef prop as shown in example below to focus the 'input' element wrapped inside Styled component 'Input' on hover.
const Input = styled.input`
padding: 0.5em;
margin: 0.5em;
color: palevioletred;
background: papayawhip;
border: none;
border-radius: 3px;
`;
class Form extends React.Component {
render() {
return (
<Input
placeholder="Hover here..."
innerRef={x => { this.input = x }}
onMouseEnter={() => this.input.focus()}
/>
);
}
}
render(
<Form />
);
NOTE:-
String refs not supported (i.e. innerRef="node"), since they're already deprecated in React.

Resources