우선 CRA를 생성하고, styled-components를 설치해줍니다.
npm install --save styled-components
그리고 pakage.json에 등록하여 styled-components를 사용한다는 것을 pakage.json에 등록하여 줍니다.
{
"resolutions": {
"styled-components": "^5"
}
}
업데이트가 자주되어 영상강의와는 다른 부분이 있기 때문에, 공식문서의 basic 부분을 따라하면서 기초를 익혀보도록 하겠습니다.
styled components를 적용하지 않았다면 아래의 소스처럼 js와 css를 분리해서 작성한 다음 import 하여 적용시켰을겁니다.
div { padding: 4em; background: papayawhip; } h1 { font-size: 1.5em; text-align: center; color: palevioletred; }
<section> <h1> Hello Wolrd! </h1> </section>
하지만 Styled components를 적용하여 개발하게 된다면 하기의 소스코드 처럼 변경할 수 있습니다.
// styled-components 라이브러리에서 import 해온 styled라는 객체를 이용하여 // 아래와 같이 h1 태그를 만들어 Title이라는 스타일드 컴포넌트를 생성 import React from "react"; import styled from "styled-components"; const Title = styled.h1` font-size: 1.5em; text-align: center; color: palevioletred; `; const Wrapper = styled.section` padding: 4em; background: papayawhip; `; function App() { return ( <Wrapper> <Title>Hello Wolrd!</Title> </Wrapper> ); } export default App;
해당 항목은 가장 기본적이면서 많이 쓰이는 항목입니다. 임포트해온 스타일드 객체를 이용하여 button tag를 만들어 Button이라는 스타일드 컴포넌트를 생성하였습니다.
import React from "react"; import styled from "styled-components"; const Button = styled.button` background: ${(props) => (props.primary ? "palevioletred" : "white")}; color: ${(props) => (props.primary ? "white" : "palevioletred")}; font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px; `; function App() { return ( <div> <Button> Normal </Button> <Button primary>Primary</Button> </div> ); } export default App;
해당 항목은 가장 기본적이면서 많이 쓰이는 항목입니다. 임포트해온 스타일드 객체를 이용하여 button tag를 만들어 Button이라는 스타일드 컴포넌트를 생성하였습니다.
import React from "react"; import styled from "styled-components"; const Button = styled.button` background: ${(props) => (props.primary ? "palevioletred" : "white")}; color: ${(props) => (props.primary ? "white" : "palevioletred")}; //Button에 primary라는 props를 주어 해당 props가 true일 경우와 false일 경우로 // 변동되는 값으로 관리하고 있습니다. font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px; `; function App() { return ( <div> <Button> Normal </Button> <Button primary>Primary</Button> </div> ); } export default App;
1번은 첫번째로 Button 스타일드 컴포넌트를 생성하고 똑같은 Button 스타일드 컴포넌트를 그대로 불러와 color값과 border-color값만 변경해서 사용하도록 하는 예제입니다. 이미 만들었던 스타일드 컴포넌트를 그대로 불러와 변경하고 싶은 부분만 변경해서 그대로 사용하도록 하였습니다.
import React from "react"; import styled from "styled-components"; const Button = styled.button` color: palevioletred; font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px; `; const TomatoButton = styled(Button)` color: tomato; border-color: tomato; `; function App() { return ( <div> <Button> Normal Button </Button> <TomatoButton>Tomato Button</TomatoButton> </div> ); } export default App;
2번은 button 태그를 as를 사용하여 a태그와 혼합한 것을 확인할 수 있습니다. 이미 태그가 지정되어 있어도 as를 사용하여 스타일을 사용하는 요소를 동적으로 변경하여 사용할 수 있습니다.
import React from "react"; import styled from "styled-components"; const Button = styled.button` display: inline-block; color: palevioletred; font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px; display: block; `; const TomatoButton = styled(Button)` color: tomato; border-color: tomato; `; function App() { return ( <div> <Button> Normal Button </Button> <Button as="a" href="/"> Link with Button style </Button> <TomatoButton as="a" href="/"> Link with Tomato Button styles </TomatoButton> </div> ); } export default App;
3번은 props로 전달받은 값을 사용자가 원하는 내용으로 변경할 수 있습니다. 해당 예제는 전달받은 props를 분리해서 거꾸로 출력하도록 만들고 있습니다.
import React from "react"; import styled from "styled-components"; const Button = styled.button` display: inline-block; color: palevioletred; font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px; display: block; `; const ReversedButton = (props) => ( <Button {...props} children={props.children.split("").reverse()} /> ); function App() { console.log(ReversedButton); return ( <div> <Button> Normal Button </Button> <Button as={ReversedButton}> Link with Normal Button styles </Button> </div> ); } export default App;
해당 예제에서는 Link와 Styled Link의 차이를 살펴 볼 수 있습니다. 그냥 Link 태그를 지정하고 개발자도구를 살펴보면 Link태그는 a태그로 변경되고 className이 없는것을 확인 할 수 있습니다. 하지만 StyledLink 태그에서는 className이 들어가 있는 것을 확인 할 수 있습니다.
import React from "react"; import styled from "styled-components" const Button = styled.button` color: palevioletred; font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px; `; const TomatoButton = styled(Button)` color: tomato; border-color: tomato; `; function App() { return ( <div> <Button> Normal Button </Button> <TomatoButton> Tomato Button </TomatoButton> </div> ); } export default App;
해당 예제에서는 inputColor가 있다면 색이 바뀌고, 없다면 palevioletred가 출력되는 것을 확인 할 수 있습니다. 또한 inputColor가 아닌 비표준 속성들도 자동으로 필터링되어 적용되는 것을 확인 할 수 있습니다. Input에 value값이 없다면 defaultValue가 적용되고 type도 적용됩니다.
import React from "react"; import styled from "styled-components"; const Input = styled.input` padding: 0.5em; margin: 0.5em; color: ${(props) => props.inputColor || "palevioletred"}; background: papayawhip; border: none; border-radius: 3px; `; function App() { return ( <div> <Input defaultValue="@probablyup" type="text" /> <Input defaultValue="@ge" type="text" inputColor="rebeccapurple" /> </div> ); } export default App;
해당 예제에서는 Counter 컴포넌트와 겹치지 않게 앞에 Styled만 추가하여 이름을 충돌시키지 않도록 구현하였습니다. 공식문서에서는 state로 관리하였지만 우리는 기존에 배웠던 useReducer를 적용시켜 구현해보았습니다.
import React, { useReducer, useCallback } from "react"; import styled from "styled-components"; const initialState = { count: 0, }; function reducer(state, action) { switch (action.type) { case "INCREASE": return { ...state, count: action.count + 1, }; case "DECREASE": return { ...state, count: action.count - 1, }; case "RESET": return { ...state, count: 0, }; default: throw new Error("IdontKnow"); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); const { count } = state; const onIncrement = useCallback(() => { dispatch({ type: "INCREASE", count, }); }, [count]); const onDecrement = useCallback(() => { dispatch({ type: "DECREASE", count, }); }, [count]); const onReset = useCallback(() => { dispatch({ type: "RESET", count, }); }, [count]); const StyledCounter = styled.div` /* ... */ `; const Paragraph = styled.p` /* ... */ `; const Button = styled.button` /* ... */ `; return ( <StyledCounter> <Paragraph>{count}</Paragraph> <Button onClick={onIncrement}>+</Button> <Button onClick={onDecrement}>-</Button> <Button onClick={onReset}>Reset</Button> </StyledCounter> ); } export default Counter;