Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using useState-hook to alter styled-components from within

I am making a component library to use in a project of mine - I've worked quite a bit with styled-components before and it's my preferred way of applying styles to my components. What I like best about it is the ability to make my components fully functional and self-contained.

I have one problem that I haven't really been able to solve satisfactory though.

I want to do something like this, but whatever I do I can't seem to access or set the props from within the styled-component.

import React, { useState } from 'react';
import styled from 'styled-components';

const Button = ({ className }) => {
  const [clicked, setClicked] = useState(false);
  return (
    <button className={className} clicked={clicked} onClick={() => setClicked(!clicked)}>
      {this.props.children}
    </button>
  );
};

export default styled(Button)`
  ${applySomeStyle}
  ${props => props.clicked} {
    ${applySomeOtherStyle}
  }
`;

I have been able to 'solve' it by doing this, but it seems incredibly redundant to create a dummy component just for this purpose. It would seem more natural to just be able to do what I do in example #1.

import React, { useState } from 'react';
import styled from 'styled-components';

const Dummy = styled.button``;

const Button = ({ className }) => {
  const [clicked, setClicked] = useState(false);
  return (
    <Dummy className={className} clicked={clicked} onClick={() => setClicked(!clicked)}>
      {this.props.children}
    </Dummy>
  );
};

export default styled(Button)`
  ${applySomeStyle}
  ${Dummy} {
     ${props => props.clicked} {
       ${applySomeOtherStyle}
     }
  }
`;

EDIT: The suggested linked issues are not applicable. The first linked issue is a person essentially asking how to pass props to his child components. The second issue is similar, but the answers are outdated because it predates the useState hook which allow us to not use Class components (The answer to the issue is basically saying that styled-components can't be used in Class components).

like image 776
Aasmund Berge Endresen Avatar asked Sep 16 '25 16:09

Aasmund Berge Endresen


2 Answers

styled() cannot refer to inner state. It does not matter if it's class and this.state or function and useState hook. The only way to handle that is splitting component into two: first to handle state changes and another one to encapsulate changes based on props.

import React, { useState } from 'react';
import styled from 'styled-components';

const InnerButton = styled(button)`
  ${props => props.clicked} {
    ${applySomeOtherStyle}
  }
`;

const Button = ({ className }) => {
  const [clicked, setClicked] = useState(false);
  return (
    <InnerButton className={className} clicked={clicked} onClick={() => setClicked(!clicked)}>
      {this.props.children}
    </InnerButton>
  );
};

export default styled(Button)`
  ${applySomeStyle}
`;
like image 112
skyboyer Avatar answered Sep 19 '25 06:09

skyboyer


Have you tried using the hook to conditionally set the className? Something like this...

import React, { useState } from 'react';
import styled from 'styled-components';

const ButtonStyled = styled('button')`
  .className {
    color: red;
}
`
export const Button = () => {
  const [clicked, setClicked] = useState(false);
  return (
    <ButtonStyled className={clicked ? 'className' : ''} clicked={clicked} onClick={() => setClicked(!clicked)}>
      {this.props.children}
    </ButtonStyled>
)}

Or, if you import the css property from styled-components, you can do something like this....

import React, { useState } from 'react';
import styled, { css } from 'styled-components'

const ButtonStyled = styled('button')`
  ${({ clicked }) =>
    clicked &&
    css`
      color: red;
    }
`
export const Button = () => {
  const [clicked, setClicked] = useState(false);
  return (
    <ButtonStyled clicked={clicked} onClick={() => setClicked(!clicked)}>
      {this.props.children}
    </ButtonStyled>
)}
like image 24
asking for a friend Avatar answered Sep 19 '25 07:09

asking for a friend