Dynamic하게 style 반영하기
inline으로 css를 반영하는 것은 기존 css 요소를 모두 덮어쓸 수 있을 뿐만 아니라, 코드의 중복이 일어날 확률이 높으므로 지양하는 것이 좋다. 즉 className에 접근하여 상태에 따라 'invalid' 라는 클래스명을 추가/제거해주면서 동적으로 css를 추가하는 것이 바람직할 것이다.
form-control 클래스명에 invalid가 추가되는 경우, 유저가 아무것도 입력하지 않았을 때 경고 처리를 해주기 위해 다음과 같이 CSS를 작성했다.
.form-control.invalid input {
border-color: red;
background: #ffd7d7;
}
.form-control.invalid label {
color: red;
}
유저가 제대로 된 값을 입력하기 시작한 경우 다시 isValid state를 true로, 제출 버튼 클릭 시 아무것도 입력하지 않은 경우 isValid state를 false로 바꿔주는 로직을 추가했다. 또한 form-control 클래스명을 `` (백틱) 으로 전달하여 ${} 문법을 통해 내부에 JS 코드를 작성할 수 있도록 했다.
import React, { useState } from "react";
import Button from "../../UI/Button/Button";
import "./CourseInput.css";
const CourseInput = (props) => {
const [enteredValue, setEnteredValue] = useState("");
const [isValid, setIsValid] = useState(true);
const goalInputChangeHandler = (event) => {
if (event.target.value.trim().length !== 0) {
setIsValid(true);
}
setEnteredValue(event.target.value);
};
const formSubmitHandler = (event) => {
event.preventDefault();
if (event.target.value.trim().length === 0) {
setIsValid(false);
return;
}
props.onAddGoal(enteredValue);
};
return (
<form onSubmit={formSubmitHandler}>
<div className={`form-control ${!isValid ? 'invalid' : ''}`}>
<label>Course Goal</label>
<input type="text" onChange={goalInputChangeHandler} />
</div>
<Button type="submit">Add Goal</Button>
</form>
);
};
export default CourseInput;
Styled Components 사용하기
한 컴포넌트에만 적용할 수 있는, scoped styling을 적용하기 위해 사용해보자. css파일을 import해오는 방식은 얼마든지 다른 요소에 영향을 미칠 수 있으니까 말이다. 수많은 개발자들이 함께 작업하는 파일에서 실수로 클래스명이 겹치는 악몽은 직접 겪지 말자.
https://styled-components.com/
위 페이지에서 보다 자세한 정보를 볼 수 있다. 일단은 우리의 프로젝트에 설치하자.
npm install --save styled-components
위 명령어를 통해 설치할 수 있다.
기존에 버튼 컴포넌트를 관리하던 Button.js Button.css파일을 styled-components를 사용하여 하나의 파일로 합쳐보자.
기존 파일은 다음과 같다.
import React from 'react';
import './Button.css';
const Button = props => {
return (
<button type={props.type} className="button" onClick={props.onClick}>
{props.children}
</button>
);
};
export default Button;
.button {
font: inherit;
padding: 0.5rem 1.5rem;
border: 1px solid #8b005d;
color: white;
background: #8b005d;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.26);
cursor: pointer;
}
.button:focus {
outline: none;
}
.button:hover,
.button:active {
background: #ac0e77;
border-color: #ac0e77;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.26);
}
styled component는 styled.___ 문법으로 사용 가능하다. styled. 뒤에 올 수 있는 것들은 전부 HTML 태그들이다. styled component는 JSX element를 반환하며 해당 요소 안에 css를 적용하는 방법은 ``을 사용하여 그 안에 코드를 넣어주면 된다. 하지만 css selector가 필요 없으므로 생략하거나 추가로 작성하고자 하는 경우 & 연산자를 사용한다. 적용된 모습을 보면 다음과 같다.
import styled from "styled-components";
const Button = styled.button`
font: inherit;
padding: 0.5rem 1.5rem;
border: 1px solid #8b005d;
color: white;
background: #8b005d;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.26);
cursor: pointer;
&:focus {
outline: none;
}
&:hover,
&:active {
background: #ac0e77;
border-color: #ac0e77;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.26);
}
`;
export default Button;
코드를 바꾸더라도 아무런 문제 없이 프로젝트는 잘 작동한다. 그리고 잊지말자! styled가 반환하는 객체는 JSX element이므로 변수는 대문자로 시작해야 한다.
Styled components & Dynamic props
앞서 form-control 부분 역시 styled-components를 적용해보자. 기존 코드는 다음과 같다.
.form-control {
margin: 0.5rem 0;
}
.form-control label {
font-weight: bold;
display: block;
margin-bottom: 0.5rem;
}
.form-control input {
display: block;
width: 100%;
border: 1px solid #ccc;
font: inherit;
line-height: 1.5rem;
padding: 0 0.25rem;
}
.form-control input:focus {
outline: none;
background: #fad0ec;
border-color: #8b005d;
}
.form-control.invalid input {
border-color: red;
background: #ffd7d7;
}
.form-control.invalid label {
color: red;
}
import React, { useState } from "react";
import Button from "../../UI/Button/Button";
import "./CourseInput.css";
const CourseInput = (props) => {
const [enteredValue, setEnteredValue] = useState("");
const [isValid, setIsValid] = useState(true);
const goalInputChangeHandler = (event) => {
if (event.target.value.trim().length !== 0) {
setIsValid(true);
}
setEnteredValue(event.target.value);
};
const formSubmitHandler = (event) => {
event.preventDefault();
if (enteredValue.trim().length === 0) {
setIsValid(false);
return;
}
props.onAddGoal(enteredValue);
};
return (
<form onSubmit={formSubmitHandler}>
<div className={`form-control ${!isValid ? "invalid" : ""}`}>
<label>Course Goal</label>
<input type="text" onChange={goalInputChangeHandler} />
</div>
<Button type="submit">Add Goal</Button>
</form>
);
};
export default CourseInput;
styled-components를 적용하면 다음과 같다. nested된 요소 역시 & 연산자를 사용하면 된다.
import React, { useState } from "react";
import styled from "styled-components";
import Button from "../../UI/Button/Button";
import "./CourseInput.css";
const FormControl = styled.div`
margin: 0.5rem 0;
& label {
font-weight: bold;
display: block;
margin-bottom: 0.5rem;
}
& input {
display: block;
width: 100%;
border: 1px solid #ccc;
font: inherit;
line-height: 1.5rem;
padding: 0 0.25rem;
}
& input:focus {
outline: none;
background: #fad0ec;
border-color: #8b005d;
}
&.invalid input {
border-color: red;
background: #ffd7d7;
}
&.invalid label {
color: red;
}
`;
const CourseInput = (props) => {
const [enteredValue, setEnteredValue] = useState("");
const [isValid, setIsValid] = useState(true);
const goalInputChangeHandler = (event) => {
if (event.target.value.trim().length !== 0) {
setIsValid(true);
}
setEnteredValue(event.target.value);
};
const formSubmitHandler = (event) => {
event.preventDefault();
if (enteredValue.trim().length === 0) {
setIsValid(false);
return;
}
props.onAddGoal(enteredValue);
};
return (
<form onSubmit={formSubmitHandler}>
<FormControl>
<label>Course Goal</label>
<input type="text" onChange={goalInputChangeHandler} />
</FormControl>
<Button type="submit">Add Goal</Button>
</form>
);
};
export default CourseInput;
자 그러나 className을 dynamic하게 넘겨주던 부분이 사라졌다. 이 부분도 다시 완성해보자. 물론 form-control이라는 클래스명은 더 이상 적어줄 필요가 없으니 다음과 같이 적어주기만 하면 된다. styled component로 만든 요소 역시 JSX element이므로 className 속성을 사용할 수 있다.
<FormControl className={!isValid && 'invalid'}>
이 방식이 조금 더 클래식한 방식이라면, props를 사용하여 바꿔줄 수도 있다. 이 방식이 조금 더 css상으로는 깔끔하게 보일 것이다.
<FormControl isvalid={!isValid}>
위와 같이 FormControl 요소에 isvalid(boolean 형) props를 넘겨줄 수도 있다! 그러면 이제 우리는 props.isvalid값에 따라 css를 다르게 작용해줄 수 있다. 왜냐하면 div 뒤에 css를 적어줄 때 `` 을 사용했기 때문에 ${}를 사용하여 얼마든지 js 구문을 쓸 수 있다.
import React, { useState } from "react";
import styled from "styled-components";
import Button from "../../UI/Button/Button";
import "./CourseInput.css";
const FormControl = styled.div`
margin: 0.5rem 0;
& label {
font-weight: bold;
display: block;
margin-bottom: 0.5rem;
color: ${(props) => (props.isvalid ? "red" : "black")};
}
& input {
display: block;
width: 100%;
border: 1px solid ${(props) => (props.isvalid ? "red" : "#ccc")};
background: ${(props) => (props.isvalid ? "#ffd7d7" : "transparent")};
font: inherit;
line-height: 1.5rem;
padding: 0 0.25rem;
}
& input:focus {
outline: none;
background: #fad0ec;
border-color: #8b005d;
}
`;
const CourseInput = (props) => {
const [enteredValue, setEnteredValue] = useState("");
const [isValid, setIsValid] = useState(true);
const goalInputChangeHandler = (event) => {
if (event.target.value.trim().length !== 0) {
setIsValid(true);
}
setEnteredValue(event.target.value);
};
const formSubmitHandler = (event) => {
event.preventDefault();
if (enteredValue.trim().length === 0) {
setIsValid(false);
return;
}
props.onAddGoal(enteredValue);
};
return (
<form onSubmit={formSubmitHandler}>
<FormControl isvalid={!isValid}>
<label>Course Goal</label>
<input type="text" onChange={goalInputChangeHandler} />
</FormControl>
<Button type="submit">Add Goal</Button>
</form>
);
};
export default CourseInput;
'React > 프로젝트' 카테고리의 다른 글
Expense Tracker Project (3/3) - React Rendering Lists (0) | 2022.07.22 |
---|---|
Expense Tracker Project (2/3) - React State & Event 처리 (0) | 2022.07.21 |
미니 프로젝트를 만들어보자 - Expense Tracker (0) | 2022.07.20 |
React - 로그인, 로그아웃 여부에 따라 Header 조건부 렌더링하기(1) (0) | 2021.05.11 |