state => 컴포넌트 내부에서 변경될 수 있는 값(데이터). 직접 변경할 수 없음
import React, { useState } from "react";
const InputPractice = () => {
const [inputs, setInputs] = useState({
name: "",
nickname: "",
});
const { name, nickname } = inputs;
const onChange = (e) => {
const { value, name } = e.target;
setInputs({
...inputs,
[name]: value,
});
};
const onReset = () => {
setInputs({
name: "",
nickname: "",
});
};
return (
<div>
<input name="name" placeholder="이름" onChange={onChange} value={name} />
<input
name="nickname"
placeholder="닉네임"
onChange={onChange}
value={nickname}
/>
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b>
{name} ({nickname})
</div>
</div>
);
};
export default InputPractice;
다음 코드의 onChange에서 [name] : value
라고 되어 있는데,
이는 state의 불변성을 위해 JS의 spread문법(기존의 것을 건들이지 않고 새로운 객체를 만들 때 사용)을 이용하여
기존 state를 복사한 다음 새로 추가되는 name
이라는 key를 가진 value로 설정하는 것이다.
inputs[name] = value
와 같이 직접적인 수정은 불가
순수 JS를 사용할 때 특정 DOM을 선택하기 위해서는
getElementById, querySelector와 같은 DOM Selector 함수를 사용한다.
리액트에서는 ref라는 것을 사용하며 함수형은 useRef를, 클래스형은 콜백함수나 React.createRef라는 함수를 사용한다.
다음은 Input 창에서 useRef를 사용하는 예제이다.
import React, { useRef, useState } from "react";
const InputPractice = () => {
const [inputs, setInputs] = useState({
name: "",
nickname: "",
});
const nameInput = useRef();
const { name, nickname } = inputs;
const onChange = (e) => {
const { value, name } = e.target;
setInputs({
...inputs,
[name]: value,
});
// inputs[name] = value;
};
const onReset = () => {
setInputs({
name: "",
nickname: "",
});
nameInput.current.focus();
};
return (
<div>
<input
name="name"
placeholder="이름"
onChange={onChange}
value={name}
ref={nameInput}
/>
<input
name="nickname"
placeholder="닉네임"
onChange={onChange}
value={nickname}
/>
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b>
{name} ({nickname})
</div>
</div>
);
};
export default InputPractice;
import React, { useRef, useState } from "react";
import CreateUser from "./components/CreateUser";
import UserList from "./components/UserList";
const 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: "velopert",
email: "public.velopert@gmail.com",
},
{
id: 2,
username: "tester",
email: "tester@example.com",
},
{
id: 3,
username: "liz",
email: "liz@example.com",
},
]);
const nextId = useRef(4);
const onCreate = () => {
/* 로직 추가 필요 */
setInputs({
username: "",
email: "",
});
nextId.current += 1;
};
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} />
</>
);
};
export default App;
import React from "react";
const 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;
const User = ({ user }) => {
return (
<div>
<b>{user.username}</b> <span>({user.email})</span>
</div>
);
};
const UserList = ({ users }) => {
return (
<div>
{users.map((user) => (
<User user={user} key={user.id} />
))}
</div>
);
};
export default UserList;
배열을
state
로 선언 → 직접 수정하면 ❌❌❌
JS 배열 함수 중push
,splice
등은 배열을 직접 수정하게 된다!
따라서 전에 배웠던 spread 문법을 사용하거나concat
함수를 사용하면 된다.
const onCreate = () => {
const user = {
id: nextId.current,
username,
email
};
setUsers([...users, user]);
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
const onCreate = () => {
const user = {
id: nextId.current,
username,
email
};
setUsers(users.concat(user));
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
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;
import React from 'react';
const User = ({ user, onRemove }) => {
return (
<div>
<b>{user.username}</b> <span>({user.email})</span>
<button onClick={() => onRemove(user.id)}>삭제</button>
</div>
);
}
const UserList = ({ users, onRemove }) => {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} onRemove={onRemove} />
))}
</div>
);
}
export default UserList;
const onRemove = id => {
// user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
// = user.id 가 id 인 것을 제거함
setUsers(users.filter(user => user.id !== id));
};
const [users, setUsers] = useState([
{
id: 1,
username: 'velopert',
email: 'public.velopert@gmail.com',
active: true
},
{
id: 2,
username: 'tester',
email: 'tester@example.com',
active: false
},
{
id: 3,
username: 'liz',
email: 'liz@example.com',
active: false
}
]);
const onToggle = id => {
setUsers(
users.map(user =>
user.id === id ? { ...user, active: !user.active } : user
)
);
};
import React from 'react';
const User = ({ user, onRemove, onToggle }) => {
return (
<div>
<b
style={{
cursor: 'pointer',
color: user.active ? 'green' : 'black'
}}
onClick={() => onToggle(user.id)}
>
{user.username}
</b>
<span>({user.email})</span>
<button onClick={() => onRemove(user.id)}>삭제</button>
</div>
);
}
const UserList = ({ users, onRemove, onToggle }) => {
return (
<div>
{users.map(user => (
<User
user={user}
key={user.id}
onRemove={onRemove}
onToggle={onToggle}
/>
))}
</div>
);
}
export default UserList;
컴포넌트는 대부분 아래 3가지 경우에 업데이트를 한다.
- props 가 바뀔 때
- state 가 바뀔 때
- 부모 컴포넌트가 리렌더링 될 때
클래스 컴포넌트에는 이럴 때 조건에 맞게 컴포넌트를 처리하고 싶을 때 사용하는 Life Cycle 메소드가 있다. (componentDidMount
, componentDidUpdate
, componentWillUnmount
등)
함수형 컴포넌트에는 따로 Life Cycle 메소드가 없고 useEffect
hook 으로 같은 기능 가능.
useEffect(() => {
// 함수 내용
}, [deps])
useEffect
를 사용 할 때에는 첫번째 파라미터에는 함수,
두번째 파라미터에는 의존값이 들어있는 배열 (deps
)을 넣는다.
만약에 deps
가 빈 배열이면, 컴포넌트가 처음 나타날때에만 useEffect
에 등록한 함수가 호출.
useEffect
에서는 함수를 반환 할 수 있는데 이를 cleanup
함수라고 부른다.
cleanup
함수는 useEffect
에 대한 뒷정리를 하는 역할을 하며
deps
가 비어있는 경우에는 컴포넌트가 사라질 때 cleanup
함수가 호출된다.
내부 발생 연산을 최적화시킬 수 있는 hook.
App.js 의
onCreate
,onRemove
,onToggle
함수는 컴포넌트가 리렌더링 될 때 마다 새로 만들어진다.
컴포넌트의 렌더링이 자주 발생하거나 렌더링해야 할 컴포넌트의 개수가 많아지면 최적화가 필요.
함수 안에서 사용하는 상태 혹은 props 가 있다면 꼭,deps
배열안에 포함시켜야 한다!