😁 useState
import React, { useState } from "react";
export default function Counter() {
const [value, setValue] = useState(0);
return (
<div>
<h1>The number is {value}</h1>
<button
onClick={() => {
setValue(value + 1);
}}
>
+1
</button>
<button
onClick={() => {
setValue(value - 1);
}}
>
-1
</button>
</div>
);
}
😃 useEffect
- 맨 처음에만 한번 실행되기를 원한다면 useEffect 2번째 인자에 빈 배열을 넣는다
- 특정 값이 변결될 때만 호출하고 싶다면, 2번째 인자에 그 변수를 포함한 배열을 넣는다
- props, state 모두 가능
- useEffet는 기본적으로 렌더링되고 난 직후마다 실행된다.
- 컴포넌트가 언마운트되기 전이나 업데이트되기 직전에 어떠한 작업을 수행하고 싶다면,
- 뒷정리 함수(cleanup)를 반환해 주어야 한다.
- useEffect 첫번째 인자의 반환값에 return ()=>{}이 cleanup 함수이다.
import React, { useState, useEffect } from "react";
export default function Info() {
const [name, setName] = useState("");
const [nickname, setNickname] = useState("");
useEffect(() => {
console.log("effect");
console.log(name);
return () => {
console.log("cleanup");
console.log(name);
};
});
const onChangeName = (e) => {
setName(e.target.value);
};
const onChangeNickname = (e) => {
setNickname(e.target.value);
};
return (
<div>
<div>
<input value={name} onChange={onChangeName} />
<input value={nickname} onChange={onChangeNickname} />
</div>
<div>
<b>name:</b> {name}
<b>nickname:</b> {nickname}
</div>
</div>
);
}
import React, { useState } from "react";
import Info from "./Info";
import "./styles.css";
const App = () => {
const [visible, setVisible] = useState(false);
return (
<div className="App">
<h1>New App</h1>
<h1>Please, Work For Me</h1>
<button
onClick={() => {
setVisible(!visible);
}}
>
{visible ? "hide" : "show"}
</button>
{visible && <Info />}
</div>
);
};
export default App;
🙄 useReducer
- useReducer를 사용했을 때의 가장 큰 장점은 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있다는 것
- useState와 거의 비슷하다.
- 이후 Redux에서 같은 방식의 작업을 한다.
import React, { useReducer } from "react";
function reducer(state, action) {
switch (action.type) {
case "INCREMENT":
return { value: state.value + 1 };
case "DECREMENT":
return { value: state.value - 1 };
default:
return state;
}
}
export default function ForReducer() {
const [state, dispatch] = useReducer(reducer, { value: 0 });
return (
<div>
<h1>The number is {state.value}</h1>
<button
onClick={() => {
dispatch({ type: "INCREMENT" });
}}
>
+1
</button>
<button
onClick={() => {
dispatch({ type: "DECREMENT" });
}}
>
-1
</button>
</div>
);
}
import React, { useReducer } from "react";
function reducer(state, action) {
return {
...state,
[action.name]: action.value
};
}
export default function ForReducer2() {
const [state, dispatch] = useReducer(reducer, {
name: "",
nickname: ""
});
const { name, nickname } = state;
const onChange = (e) => {
dispatch(e.target);
};
return (
<div>
<div>
<input name="name" value={name} onChange={onChange} />
<input name="nickname" value={nickname} onChange={onChange} />
</div>
<div>
<b>name:</b> {name}
<b>nickname:</b> {nickname}
</div>
</div>
);
}
😏 useMemo
- useMoemo를 사용하면 컴포넌트 내부에서 발생하는 연산을 최적화 할 수 있다.
- 아래에는 기존의 useState를 사용한 평균값 계산 컴포넌트가 있다.
import React, { useState } from "react";
const getAverage = (numbers) => {
console.log("calculating now");
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
export default function Average() {
const [list, setList] = useState([]);
const [number, setNumber] = useState("");
const onChange = (e) => {
setNumber(e.target.value);
};
const onInsert = (e) => {
const nextList = list.concat(parseInt(number, 10));
setList(nextList);
setNumber("");
};
return (
<div>
<h1>Average</h1>
<input value={number} onChange={onChange} />
<button onClick={onInsert}>Enroll</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값:</b> {getAverage(list)}
</div>
</div>
);
}
- 이를 실행하면 우리가 숫자를 등록할 때뿐만 아니라 인풋 내용이 수정될 때에도 우리가 만든 getAverage 함수가 호출되는 것을 확인할 수 있다.
- 특정 값이 바뀌었을 때만 연산이 실행될 수 있게 useMemo를 사용해보자.
import React, { useState, useMemo } from "react";
const getAverage = (numbers) => {
console.log("calculating now");
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
export default function Average() {
const [list, setList] = useState([]);
const [number, setNumber] = useState("");
const onChange = (e) => {
setNumber(e.target.value);
};
const onInsert = (e) => {
const nextList = list.concat(parseInt(number, 10));
setList(nextList);
setNumber("");
};
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<h1>Average</h1>
<input value={number} onChange={onChange} />
<button onClick={onInsert}>Enroll</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값:</b> {avg}
</div>
</div>
);
}
😙 useCallback
- 이벤트 핸들러 함수를 필요할 때만 생성할 수 있다.
- 방금 위 onChange와 onInsert 함수는 새로 렌더링 할 때마다 매번 새로 생긴다.
- 이런 부분을 최적화 시켜줄 수 있다.
- useCallback은 useMemo를 사용해 똑같이 구현할 수 있다.
- 일반적으로 useMemo는 그 결괏값을 반환하는데 반해 useMemo로 함수를 반환한다면,
- 이는 useCallback과 같은 기능을 하는 것이다
useCallback(()=>{
console.log('hello World!')
},[])
useMemo(()=>{
const fn = () => {
console.log('hello World!')
};
return fn;
},[])
import React, { useState, useMemo, useCallback } from "react";
const getAverage = (numbers) => {
console.log("calculating now");
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
export default function Average() {
const [list, setList] = useState([]);
const [number, setNumber] = useState("");
const onChange = useCallback((e) => {
setNumber(e.target.value);
}, []);
const onInsert = useCallback(
(e) => {
const nextList = list.concat(parseInt(number, 10));
setList(nextList);
setNumber("");
},
[number, list]
);
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<h1>Average</h1>
<input value={number} onChange={onChange} />
<button onClick={onInsert}>Enroll</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값:</b> {avg}
</div>
</div>
);
}
😋 useRef
- ref 기능을 사용하고자 할 때 쓸 수 있다.
- 컴포넌트의 로컬 변수를 사용해야 할 때도 사용할 수 있다.
- useRef의 객체는 렌더링에 영향을 미치지 않는다.
import React, {useRef} from "react"
const RefSample = () => {
const id = useRef(1);
const setId = (n) => {
id.current = n;
}
const printId = () => {
console.log(id.current)
}
return (
<div>
refsample
</div>
)
}
export default RefSample;
😜 Custom Hooks
import { useReducer } from "react";
function reducer(state, action) {
return {
...state,
[action.name]: action.value
};
}
export default function useInputs(initialForm) {
const [state, dispatch] = useReducer(reducer, initialForm);
const onChange = (e) => {
dispatch(e.target);
};
return [state, onChange];
}
import React from "react";
import useInputs from "./useInputs";
export default function Info() {
const [state, onChange] = useInputs({
name: "",
nickname: ""
});
const { name, nickname } = state;
return (
<div>
<div>
<input value={name} onChange={onChange} />
<input value={nickname} onChange={onChange} />
</div>
<div>
<b>name:</b> {name}
<b>nickname:</b> {nickname}
</div>
</div>
);
}