기존의 컴포넌트에서 State와 생명주기 관리를 위해선 반드시 클래스 컴포넌트를 사용해야 했음
클래스 컴포넌트는 사용하기 다소 난해할 수 있으며, 이를 보완하면서 함수 컴포넌트에서 클래스 컴포넌트의 기능을 구현하기 위해 React 16.8 버전에서 추가됨
컴포넌트 내 동적인 데이터를 관리하는 hook
state는 읽기 전용으로, 직접 수정하면 안됨 ( 직접 수정할 경우, 재렌더링이 되지 않음 )
- 따라서, state를 변경하기 위해선 setState를 사용해야 함
- setState가 실행되면, 자동으로 컴포넌트 재렌더링 실시
부모 컴포넌트의 state가 갱신될 때마다, 자식 컴포넌트에서도 바뀐 state 값으로 재랜더링하도록 할 수 있음.
// App.js
import React, { useState } from 'react';
import Greeting from './components/Greeting'
function App() {
const [ user, setUser ] = useState("")
return (
<div className="App">
<input value={user} onChange={(event) => {
setUser(event.target.value);
}} />
<Greeting name={user}/>
</div>
);
}
export default App;
// Greeting.js
import React from 'react';
function Greeting({name}) {
return <h1>{name}님 안녕하세요.</h1>
}
export default Greeting;
Object 멤버의 값을 변경했을 때, react는 재랜더링을 실행하지 않음.
따라서, 멤버가 변경될 경우에도 Object 자체를 갱신하도록 해야함.
import React, { useState } from 'react';
function App() {
const [ student, setStudent ] = useState({
name: "김민수",
count: 0
})
return (
<div className="App">
<span>{student.name}님이 버튼을 {student.count}회 클릭하였습니다.</span><br />
<button onClick={() => {
setStudent((current) => {
const newStudent = {...current}
newStudent.count += 1
return newStudent
})
}}>button</button>
</div>
);
}
함수 컴포넌트에서 side effect 수행 가능
const App () => { useEffect(EffectCallback, Deps?) }
EffectCallback: Deps에 지정된 변수가 변경될 때 실행할 함수
Deps: 변경을 감지할 변수들의 집합(배열)
EffectCallback 함수가 호출되는 경우Deps 를 빈 배열로 둘 경우
- 컴포넌트 생성 시 최초에 1회 호출됨
useEffect 내에서 다른 함수를 return 할 경우
- state가 변경되어 컴포넌트가 다시 렌더링 되기 전 호출
- 컴포넌트 소멸 시 호출됨
// App.js
import React, { useState } from 'react';
import Greeting from './components/Greeting';
function App () {
const [isCreated, setIsCreated] = useState(false);
const handleCreate = () => {
setIsCreated((curr) => {
return !curr;
});
};
return
<div>
<button onClick={handleCreate}>
클릭 !
</button>
{isCreated && <Greeting />}
</div>
}
// Greeting.js
import React, { useEffect } from "react";
function Greeting() {
useEffect(() => {
console.log("컴포넌트가 생성되었습니다.");
return () => {
console.log("컴포넌트가 소멸되었습니다.");
};
}, []);
return <h1>안녕하세요</h1>;
}
export default Greeting;
변수를 메모
지정한 State나 Props가 변경될 때 해당 값을 활용해 계산된 값을 메모이제이션하여 재렌더링 시 불필요한 연산을 줄임
const App () => {
const [firstName, setFirstName] = useState('정글')
const [lastName, setlastName] = useState('김')
// 이름과 성이 바뀔 때마다 풀네임을 메모이제이션
const fullName = useMemo(()=> {
return `${firstName} ${lastName}`
}, [firstName, lastName]);
}
import React, {useState, useMemo} from 'react';
const App = () => {
const [foo, setFoo] = useState(0)
const [bar, setBar] = useState(0)
const multi = useMemo(() => {
let sum = foo*bar
return sum
}, [foo, bar])
return (
<div className="App">
<input value={foo} onChange={(event)=>{setFoo(parseInt(event.target.value))}} />
<input value={bar} onChange={(event)=>{setBar(parseInt(event.target.value))}} />
<div>{multi}</div>
</div>
)
}
export default App;
함수를 메모
함수를 메모이제이션하여, 재렌더링 시 함수가 다시 생성되는 것을 방지
const App () => {
const [firstName, setFirstName] = useState('정글')
const [lastName, setlastName] = useState('김')
// 이름과 성이 바뀔 때마다 풀네임을 return 하는 함수를 메모이제이션
const getFullName = useCallback(()=> {
return `${firstName} ${lastName}`
}, [firstName, lastName]);
}
return <>{getFullName()}</>
import React, { useState, useCallback } from 'react';
function App() {
const [ foo, setFoo ] = useState(0)
const [ bar, setBar ] = useState(0)
const calc = useCallback(() => {
return foo + bar
}, [ foo, bar ])
return (
<div className="App">
<input value={foo} onChange={(event)=>{
setFoo(parseInt(event.target.value))
}}/>
<input value={bar} onChange={(event)=>{
setBar(parseInt(event.target.value))
}}/>
<div>{calc()}</div>
</div>
);
}
// 아래의 useMemo와 useCallback은 동일하게 작동함
useMemo(()=> fn, deps)
useCallback(fn, deps)
컴포넌트 생애 주기 내에서 유지할 ref 객체를 반환
- ref 객체는 .current 라는 속성을 가지며, 이 값을 자유롭게 변경 가능
- React에서, DOM Element에 접근할 때 사용
- useRef에 의해 반환된 ref 객체가 변경되어도 컴포넌트가 재렌더링되지 않음
const App = () => {
const inputRef = useRef(null)
const onButtonClick = () => {
inputRef.current.focus()
}
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={onButtonClick}>
input으로 포커스
</button>
</input>
</div>
)
}
import React, {useRef} from 'react';
function App() {
const inputRef = useRef(null)
return (
<div className="App">
<input ref={inputRef} />
<button onClick={() => {alert(inputRef.current.value)}}>Click!</button>
</div>
);
}
export default App;
// app.js
import useToggle from "./hooks/useToggle";
function App() {
const { isOn, toggle } = useToggle(false);
return (
<div className="App">
<button onClick={() => toggle()>
{isOn ? "켜짐" : "꺼짐"}
</button>
</div>
);
}
export default App;
// useToggle.js
function useToggle = (initValue) => {
const [isOn, setIsOn] = useState(initValue)
const toggle = () => {
setIsOn(curr => {
return !curr
})
}
return { isOn, toggle }
}
export default useToggle;