1. useRef의 기능
① DOM에 직접 접근하는 방법
② 컴포넌트 안에서 조회 및 수정할 수 있는 변수를 관리
2. useRef로 컴포넌트 안의 변수 만들기
└ 컴포넌트 안에서 조회 및 수정할 수 있는 변수를 관리
└ setTimeout, setInterval을 통해서 만들어진 id
└ 외부 라이브러리를 사용하여 생성된 인스턴스
└ scoll 위치
1) 배열의 항목 추가
** /component/userlist2.jsx
import React from 'react';
function User({user}) {
return (
<div>
<b>{user.username}</b> <span>({user.email})</span>
</div>
)
}
function UserList({users}) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id}/>
))}
</div>
)
}
export default UserList;
** /component/createUser.jsx
import React from 'react';
function CreateUser({username, email, onChange, onCreate}) {
return (
<div>
<input name="username" placeholder="이름을 입력하세요" onChange={onChange} value={username}/>
<input name="email" placeholder="이메일을 입력하세요" onChange={onChange} value={email}/>
<button onClick={onCreate}>등록</button>
</div>
)
}
export default CreateUser;
** app.jsx
import React, {useRef, useState} from 'react';
// import Counter from './component/counter';
// import Input from './component/input';
// import MultiInput from './component/multiinput';
// import UserList from './component/userlist'
import UserList from './component/userlist2';
import CreateUser from './component/createUser';
function App() {
const [inputs, setInputs] = useState({
username: '',
email: ''
});
const {username, email} = inputs;
const onChange = e => {
const {name, value} = e.target;
setInputs({
...inputs,
[name]: value
});
}
const [users, setUsers] = useState([
{
id: 1,
username: '김사과',
email: 'apple@apple.com'
},
{
id: 2,
username: '오랜지',
email: 'orange@orange.com'
},
{
id: 3,
username: '반하나',
email: 'banana@banana.com'
},
{
id: 4,
username: '이메론',
email: 'melon@melon.com'
},
{
id: 5,
username: '배애리',
email: 'berry@berry.com'
}
]);
const nextId = useRef(6);
const onCreate = () => {
const user = {
id: nextId.current,
username,
email
}
// 배열에 새 항목을 추가할 때는 spread 연산자 또는 concat 함수를 사용
// 1. spread 연산자
// setUsers([...users, user]);
// 2. concat 함수
setUsers(users.concat(user));
// --------------------------------------------------------------
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
// return (
// <div>
// {/* <Counter/> */}
// {/* <Input/> */}
// {/* <MultiInput/> */}
// {/* <UserList/> */}
// </div>
// );
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users}/>
</>
);
}
export default App;
2) 배열의 항목 제거
** /component/userlist2.jsx
import React from 'react';
function User({user, onRemove}) {
return (
<div>
<b>{user.username}</b> <span>({user.email})</span>
<button onClick={() => onRemove(user.id)}>삭제</button>
</div>
);
}
function UserList({users, onRemove}) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} onRemove={onRemove}/>
))}
</div>
)
}
export default UserList;
** app.jsx
import React, {useRef, useState} from 'react';
// import Counter from './component/counter';
// import Input from './component/input';
// import MultiInput from './component/multiinput';
// import UserList from './component/userlist'
import UserList from './component/userlist2';
import CreateUser from './component/createUser';
function App() {
const [inputs, setInputs] = useState({
username: '',
email: ''
});
const {username, email} = inputs;
const onChange = e => {
const {name, value} = e.target;
setInputs({
...inputs,
[name]: value
});
}
const [users, setUsers] = useState([
{
id: 1,
username: '김사과',
email: 'apple@apple.com'
},
{
id: 2,
username: '오랜지',
email: 'orange@orange.com'
},
{
id: 3,
username: '반하나',
email: 'banana@banana.com'
},
{
id: 4,
username: '이메론',
email: 'melon@melon.com'
},
{
id: 5,
username: '배애리',
email: 'berry@berry.com'
}
]);
const nextId = useRef(6);
const onCreate = () => {
const user = {
id: nextId.current,
username,
email
}
// 배열에 새 항목을 추가할 때는 spread 연산자 또는 concat 함수를 사용
// 1. spread 연산자
// setUsers([...users, user]);
// 2. concat 함수
setUsers(users.concat(user));
// --------------------------------------------------------------
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
const onRemove = id => {
// user.id가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듦
// user.id가 id인 것을 제거함
setUsers(users.filter(user => user.id !== id));
}
// return (
// <div>
// {/* <Counter/> */}
// {/* <Input/> */}
// {/* <MultiInput/> */}
// {/* <UserList/> */}
// </div>
// );
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove}/>
</>
);
}
export default App;
3) 항목을 클릭하면 글자색 토글되도록 설정
** userlist2.jsx
import React from 'react';
function User({user, onRemove, onToggle}) {
return (
<div>
<b
style={{
cursor: 'pointer',
color: user.active ? 'deeppink' : 'black'
}}
onClick={() => onToggle(user.id)}
>{user.username}</b> <span>({user.email})</span>
<button onClick={() => onRemove(user.id)}>삭제</button>
</div>
);
}
function UserList({users, onRemove, onToggle}) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} onRemove={onRemove} onToggle={onToggle}/>
))}
</div>
)
}
export default UserList;
** app.jsx
import React, {useRef, useState} from 'react';
// import Counter from './component/counter';
// import Input from './component/input';
// import MultiInput from './component/multiinput';
// import UserList from './component/userlist'
import UserList from './component/userlist2';
import CreateUser from './component/createUser';
function App() {
const [inputs, setInputs] = useState({
username: '',
email: ''
});
const {username, email} = inputs;
const onChange = e => {
const {name, value} = e.target;
setInputs({
...inputs,
[name]: value
});
}
const [users, setUsers] = useState([
{
id: 1,
username: '김사과',
email: 'apple@apple.com',
active: true
},
{
id: 2,
username: '오랜지',
email: 'orange@orange.com',
active: false
},
{
id: 3,
username: '반하나',
email: 'banana@banana.com',
active: false
},
{
id: 4,
username: '이메론',
email: 'melon@melon.com',
active: false
},
{
id: 5,
username: '배애리',
email: 'berry@berry.com',
active: false
}
]);
const nextId = useRef(6);
const onCreate = () => {
const user = {
id: nextId.current,
username,
email
}
// 배열에 새 항목을 추가할 때는 spread 연산자 또는 concat 함수를 사용
// 1. spread 연산자
// setUsers([...users, user]);
// 2. concat 함수
setUsers(users.concat(user));
// --------------------------------------------------------------
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
const onRemove = id => {
// user.id가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듦
// user.id가 id인 것을 제거함
setUsers(users.filter(user => user.id !== id));
}
const onToggle = id => {
setUsers(
users.map(user => user.id === id ? {...user, active: !user.active} : user)
);
}
// return (
// <div>
// {/* <Counter/> */}
// {/* <Input/> */}
// {/* <MultiInput/> */}
// {/* <UserList/> */}
// </div>
// );
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
</>
);
}
export default App;
3. useEffect
: 컴포넌트가 마운트(처음 나타났을 때) 됬을 때,
언마운트(사라질 때) 됬을 때,
그리고 업데이트(특정 props가 바뀔 때) 될 때 특정 작업을 처리할 수 있다.
** 컴포넌트가 마운트시 하는 작업
① props로 받은 값을 컴포넌트의 로컬 상태로 설정
② 외부 API 요청(REST API 등 ...)
③ 라이브러리 사용
④ setInterval을 통한 반복작업 또는 setTimeout을 통한 작업 예약
** 컴포넌트가 언마운트시 하는 작업
① 라이브러리 인스턴스 제거
② setInterval, setTimeout을 사용하여 등록한 작업을 clear 할 때
1) useEffect(함수, 배열)
└ [함수] 처음 컴포넌트가 나타날 때 실행할 함수
└ [배열] 의존값이 들어있는 배열,
만약 배열을 비우게 된다면 컴포넌트가 처음 나타낼 때만 useEffect에 등록된 함수가 호출됨
2) cleanup 함수
: useEffect에서는 함수를 반환할 수 있다.
useEffect에 대한 뒷정리를 한다.
deps가 비어있는 경우에 컴포넌트가 사라질 때, cleanup 함수가 호출된다.
** /component/userlist2.jsx
import React, {useEffect} from 'react';
function User({user, onRemove, onToggle}) {
useEffect(() => {
console.log('컴포넌트가 화면에 나타남!');
return () => {
console.log('컴포넌트가 화면에서 사라짐');
}
}, [])
return (
<div>
<b
style={{
cursor: 'pointer',
color: user.active ? 'deeppink' : 'black'
}}
onClick={() => onToggle(user.id)}
>{user.username}</b> <span>({user.email})</span>
<button onClick={() => onRemove(user.id)}>삭제</button>
</div>
);
}
function UserList({users, onRemove, onToggle}) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} onRemove={onRemove} onToggle={onToggle}/>
))}
</div>
)
}
export default UserList;
** /component/userlist2.jsx
import React, {useEffect} from 'react';
function User({user, onRemove, onToggle}) {
useEffect(() => {
// console.log('컴포넌트가 화면에 나타남!');
console.log(user);
console.log('user 값이 설정되었음!');
return () => {
// console.log('컴포넌트가 화면에서 사라짐');
console.log(user);
console.log('user가 바뀌기 전...');
}
}, [user])
return (
<div>
<b
style={{
cursor: 'pointer',
color: user.active ? 'deeppink' : 'black'
}}
onClick={() => onToggle(user.id)}
>{user.username}</b> <span>({user.email})</span>
<button onClick={() => onRemove(user.id)}>삭제</button>
</div>
);
}
function UserList({users, onRemove, onToggle}) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} onRemove={onRemove} onToggle={onToggle}/>
))}
</div>
)
}
export default UserList;
** /component/userlist2.jsx
└ deps 파라미터 생략 : 컴포넌트가 리렌더링될 때마다 무조건 호출
import React, {useEffect} from 'react';
function User({user, onRemove, onToggle}) {
// useEffect(() => {
// // console.log('컴포넌트가 화면에 나타남!');
// console.log(user);
// console.log('user 값이 설정되었음!');
// return () => {
// // console.log('컴포넌트가 화면에서 사라짐');
// console.log(user);
// console.log('user가 바뀌기 전...');
// }
// }, [user])
// deps 파라미터 생략 : 컴포넌트가 리랜더링될 때마다 무조건 호출
useEffect(() => {
console.log(user);
});
return (
<div>
<b
style={{
cursor: 'pointer',
color: user.active ? 'deeppink' : 'black'
}}
onClick={() => onToggle(user.id)}
>{user.username}</b> <span>({user.email})</span>
<button onClick={() => onRemove(user.id)}>삭제</button>
</div>
);
}
function UserList({users, onRemove, onToggle}) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} onRemove={onRemove} onToggle={onToggle}/>
))}
</div>
)
}
export default UserList;
4. useMemo
: 성능 최적화를 위해 연산된 값을 재사용한다.
** app.jsx
import React, {useRef, useState, useMemo} from 'react';
// import Counter from './component/counter';
// import Input from './component/input';
// import MultiInput from './component/multiinput';
// import UserList from './component/userlist'
import UserList from './component/userlist2';
import CreateUser from './component/createUser';
function countActiveUser(users) {
console.log('선택된 사용자 수를 세는 중 ... ');
return users.filter(user => user.active).length;
}
function App() {
const [inputs, setInputs] = useState({
username: '',
email: ''
});
const {username, email} = inputs;
const onChange = e => {
const {name, value} = e.target;
setInputs({
...inputs,
[name]: value
});
}
const [users, setUsers] = useState([
{
id: 1,
username: '김사과',
email: 'apple@apple.com',
active: true
},
{
id: 2,
username: '오랜지',
email: 'orange@orange.com',
active: false
},
{
id: 3,
username: '반하나',
email: 'banana@banana.com',
active: false
},
{
id: 4,
username: '이메론',
email: 'melon@melon.com',
active: false
},
{
id: 5,
username: '배애리',
email: 'berry@berry.com',
active: false
}
]);
const nextId = useRef(6);
const onCreate = () => {
const user = {
id: nextId.current,
username,
email
}
// 배열에 새 항목을 추가할 때는 spread 연산자 또는 concat 함수를 사용
// 1. spread 연산자
// setUsers([...users, user]);
// 2. concat 함수
setUsers(users.concat(user));
// --------------------------------------------------------------
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
const onRemove = id => {
// user.id가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듦
// user.id가 id인 것을 제거함
setUsers(users.filter(user => user.id !== id));
}
const onToggle = id => {
setUsers(
users.map(user => user.id === id ? {...user, active: !user.active} : user)
);
}
// return (
// <div>
// {/* <Counter/> */}
// {/* <Input/> */}
// {/* <MultiInput/> */}
// {/* <UserList/> */}
// </div>
// );
const count = useMemo(() => countActiveUser(users), [users]);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
<div>선택된 사용자 수 : {count}</div>
</>
);
}
export default App;
5. Sass, Scss
1) Sass
: CSS pre-processor로서 복잡한 작업을 쉽게 할 수 있게 해주고,
코드의 재활용성을 높여줄 뿐만 아니라 코드의 가독성을 높여주어 유지보수를 쉽게 해준다.
"yarn add node-sass"
** /component/button.jsx
import React from 'react';
import './button.scss';
/*
HTML에서 CSS class 적용
<div class="button size">2가지 class 적용</div>
*/
function Button({children, size}) {
return <button className={["Button", size].join(' ')}>{children}</button>;
}
Button.defaultProps = {
size: 'medium'
}
export default Button;
** /component/button.scss
$blue: #228be6;
.Button {
display: inline-flex;
color: white;
font-weight: bold;
outline: none;
border-radius: 4px;
border: none;
cursor: pointer;
// height: 2.3rem;
// padding-top: 0.5rem;
// padding-left: 1rem;
// padding-right: 1rem;
// font-size: 1rem;
&.large {
height: 3rem;
padding-top: 0.6rem;
padding-left: 1rem;
padding-right: 1rem;
font-size: 1.25rem;
}
&.medium {
height: 2.25rem;
padding-top: 0.5rem;
padding-left: 1rem;
padding-right: 1rem;
font-size: 1rem;
}
&.small {
height: 1.7rem;
padding-top: 0.3rem;
padding-left: 1rem;
padding-right: 1rem;
font-size: 0.85rem;
}
background-color: $blue; // $blue 변수값을 저장
&:hover {
background-color: lighten($blue, 10%); // 색상을 10% 밝게
}
&:active {
background-color: darken($blue, 10%); // 색상을 10% 어둡게
}
& + & {
margin-left: 1rem;
}
}
** app.scss
.App {
width: 500px;
margin: 0 auto;
margin-top: 4rem;
border: 1px solid gold;
padding: 1rem;
}
** app.jsx
import React from 'react';
import './app.scss';
import Button from './component/button'
function App() {
return (
<div className="App">
<div>
<Button size="large">버튼 1</Button>
<Button>버튼 2</Button>
<Button size="small">버튼 3</Button>
</div>
</div>
);
}
export default App;
** index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
** 색상 관련
https://yeun.github.io/open-color
** /component/button.jsx
import React from 'react';
import './button.scss';
/*
HTML에서 CSS class 적용
<div class="button size">2가지 class 적용</div>
*/
function Button({children, size, color}) {
return <button className={["Button", size, color].join(' ')}>{children}</button>;
}
Button.defaultProps = {
size: 'medium',
color: 'blue'
}
export default Button;
** /component/button.scss
$blue: #228be6;
$gray: #495057;
$pink: #f06595;
@mixin button-color($color) {
background-color: $color;
&:hover {
background-color: lighten($color, 10%);
}
&:active {
background-color: darken($color, 10%);
}
}
.Button {
display: inline-flex;
color: white;
font-weight: bold;
outline: none;
border-radius: 4px;
border: none;
cursor: pointer;
// 1. 첫번째 size 실습
// height: 2.3rem;
// padding-top: 0.5rem;
// padding-left: 1rem;
// padding-right: 1rem;
// font-size: 1rem;
// 2. 두번째 size 실습
&.large {
height: 3rem;
padding-top: 0.6rem;
padding-left: 1rem;
padding-right: 1rem;
font-size: 1.25rem;
}
&.medium {
height: 2.25rem;
padding-top: 0.5rem;
padding-left: 1rem;
padding-right: 1rem;
font-size: 1rem;
}
&.small {
height: 1.7rem;
padding-top: 0.3rem;
padding-left: 1rem;
padding-right: 1rem;
font-size: 0.85rem;
}
// 1. 첫번째 color 실습
// background-color: $blue; // $blue 변수값을 저장
// &:hover {
// background-color: lighten($blue, 10%); // 색상을 10% 밝게
// }
// &:active {
// background-color: darken($blue, 10%); // 색상을 10% 어둡게
// }
// 2. 두번째 color 실습
// &.blue {
// background-color: $blue;
// &:hover {
// background-color: lighten($blue, 10%);
// }
// &:active {
// background-color: darken($blue, 10%);
// }
// }
// &.gray {
// background-color: $gray;
// &:hover {
// background-color: lighten($gray, 10%);
// }
// &:active {
// background-color: darken($gray, 10%);
// }
// }
// &.pink {
// background-color: $pink;
// &:hover {
// background-color: lighten($pink, 10%);
// }
// &:active {
// background-color: darken($pink, 10%);
// }
// }
// 3. 세번째 color 실습
&.blue {
@include button-color($blue);
}
&.gray {
@include button-color($gray);
}
&.pink {
@include button-color($pink);
}
& + & {
margin-left: 1rem;
}
}
** app.jsx
import React from 'react';
import './app.scss';
import Button from './component/button'
function App() {
return (
<div className="App">
<div className="height">
<Button size="large">버튼 1</Button>
<Button>버튼 2</Button>
<Button size="small">버튼 3</Button>
</div>
<div className="height">
<Button size="large" color="gray">버튼 4</Button>
<Button color="gray">버튼 5</Button>
<Button size="small" color="gray">버튼 6</Button>
</div>
<div className="height">
<Button size="large" color="pink">버튼 7</Button>
<Button color="pink">버튼 8</Button>
<Button size="small" color="pink">버튼 9</Button>
</div>
</div>
);
}
export default App;
** app.scss
.App {
width: 500px;
margin: 0 auto;
margin-top: 4rem;
border: 1px solid gold;
padding: 1rem;
.height + .height {
margin-top: 1rem;
}
}
** 조건부 스타일링을 할 때 class 이름을 직접 조합해주는 것보다 편하게 작성
"yarn add classnames"
<button className="button red">
<button className={["button", "red"].join(' ')}>
classNames("Button", "red"); → Button red
classNames("Button", {red: true}); → true일 때, Button red / false일때, Button
classNames({Button: true}, {red: true}) → Button red, true일 경우만!!
** /component/button.jsx
import classNames from 'classnames';
import React from 'react';
import './button.scss';
/*
HTML에서 CSS class 적용
<div class="button size">2가지 class 적용</div>
*/
function Button({children, size, color}) {
// return <button className={["Button", size, color].join(' ')}>{children}</button>;
// classnames 사용시
return <button className={classNames("Button", size, color)}>{children}</button>;
}
Button.defaultProps = {
size: 'medium',
color: 'blue'
}
export default Button;
'웹_프론트_백엔드 > 프론트엔드' 카테고리의 다른 글
2021.04.03 (0) | 2021.05.18 |
---|---|
2021.03.28 (0) | 2021.05.16 |
2021.03.21 (0) | 2021.05.12 |
2021.03.20 (0) | 2021.05.11 |
2021.03.20 (0) | 2021.04.20 |