useReducer()는 컴포넌트의 상태를 관리할 때 사용하는 Hooks 입니다.
특징
컴포넌트 상태 업데이트 로직을 컴포넌트에서 분리 가능합니다.
다른 컴포넌트에서도 해당 로직을 재사용 가능합니다.
동작 과정
useReducer() 사용 방법
const [state, dispatch] =useReducer(reducer, initialState);
dispatch() 사용 방법
dispatch( {key: value} )
reducer()
function reducer(state, action) {
// 새로운 상태를 만드는 로직
return 새로운 상태;
}
function countReducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
export default countReducer;
import React, { useReducer } from "react";
import countReducer from './countReducer';
const Counter = () => {
const [state, dispatch] = useReducer(countReducer, 0);
function numUp() {
dispatch( {type: "INCREMENT" });
}
function numDown() {
dispatch( {type: "DECREMENT"});
}
retuern (
<div>
<p>
현재 카운터 값은 <b>{state}</b>입니다.
</p>
<button onClick={numUp}>+1</button>
<button onClick={numDown}>-1</button>
</div>
);
};
export default Counter;
function numUp() {
dispatch( {type: "INCREMENT", icon: "🏴"} );
}
function numDown() {
dispatch( {type: "DECREMENT", icon: "🏴☠️"} );
}
function countReducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return action.icon;
case 'DECREMENT':
return action.icon;
default:
return state;
}
}
{
name: "harimad",
age: 10,
job: "teacher",
skill: [
{HTML : "상", Javascript: "중"},
{Vue : "상", React: "하"}
]
}
컴포넌트 안에 함수()가 존재 합니다.
함수() 결과값을 출력하는 Component가 있습니다.
만약, 렌더링이 되면 Componenet가 호출되면서 함수()가 또 호출됩니다.
그러면 Component 의 return이 재실행 되면서 함수()의 결과값이 또 다시 출력하게 됩니다. (...👎)
아래의 함수가 있다고 가정합니다.
function MyComponent({a, b}) { // 부모로 부터 props로 a, b를 받습니다.
const result = compute(a, b); // compute함수에 인자를 넣어서 값을 저장 합니다.
return <div>{result}</div>; // 그 값을 화면에 출력 합니다. // 출력을 위해서는 compute함수가 실행될 때 까지 기다려야 합니다.
}
함수의 결과 값을 저장해서 재 렌더링이 되어도 함수 결과 값이 똑같다면, 이전에 저장한 함수 결과 값을 재활용 하면 좋지 않을까요?
그것을 가능하게 하는 함수가 useMemo() 입니다.
const memoizaedValue = useMemo(
() => {
// 연산량이 많은 작업을 수행
return compute(a, b)
}, [a, b])
useMemo(() => {
// 연산량이 많은 작업을 수행
return compute(a, b)
}, []);
useMemo(() => {
// 연산량이 많은 작업을 수행
return compute(a, b)
});
function MyComponent( {a, b} ) {
const result = useMemo(() => compute(a, b), [a, b]); // 👈 compute() 함수를 useMemo()에 감싸줍니다.
return <div>{result}</div>;
}
function hardCalculate(number) {
console.log("어려운 계산중");
for (let i = 0; i < 1000000; i++) {}
return number + 10000;
}
const useMemoComponent = () => {
const [hardNumber, setHardNumber] = useState(1);
const hardSum = hardCalculate(hardNumber); // 👎
return (
<div>
<h3>어려운 계산기</h3>
<input
type="number"
value={hardNumber}
onChange={(e) => setHardNumber(parseInt(e.target.value))}>
</input>
<span> + 10000 = {hardNumber} </span>
</div>
);
}
실행 결과
useMemoComponent.js
function hardCalculate(number) {
console.log("어려운 계산중");
for (let i = 0; i < 1000000; i++) {}
return number + 10000;
}
function easyCalculate(number) {
console.log("쉬운 계산중");
return number + 1;
}
const useMemoComponent = () => {
const [hardNumber, setHardNumber] = useState(1);
const [easyNumber, setEasyNumber] = useState(1);
const hardSum = hardCalculate(hardNumber); // 👎
const easySum = easyCalculate(easyNumber);
return (
<div>
<h3>어려운 계산기</h3>
<input
type="number"
value={hardNumber}
onChange={(e) => setHardNumber(parseInt(e.target.value))}> // 👎 state가 변경 될 때 마다 hardCalculate() 호출
</input>
<span> + 10000 = {hardSum} </span>
<h3>쉬운 계산기</h3>
<input
type="number"
value={easyNumber}
onChange={(e) => setEasyNumber(parseInt(e.target.value))}>
</input>
<span> + 1 = {easySum} </span>
</div>
);
}
실행결과
useMemo()를 사용하여 수정
const hardSum = useMemo(
() => hardCalculate(hardNumber),
[hardNumber]
);
실행결과
hardCalculate() 함수가 useMemo()에 의해서 값이 메모 되어 있기 때문에, 재렌더링이 되어도 hardCalculate() 함수가 재호출 되지 않습니다👍
useMemo 사용 시 주의점
이미 생성해 놓은 함수를 재사용할 수 있게 해주는 Hook 입니다.
useMemo()와 유사한 Hook 입니다.
컴포넌트에 useCallBack()을 사용하지 않고 함수를 정의한 경우
const MyComponent = () => {
function myFunction() {
console.log("함수 생성 완료")
}
}
useCallBack() 구조
const memoizedCallback = useCallBack(
() => doSomething(a, b),
[a, b]
)
첫 번째 매개변수
두 번째 매개변수
의존성 배열이 빈 배열인 경우
useCallBack(() => {
return doSomething(a, b)
}, []);
의존성 배열이 없는 경우
useCallBack(() => {
return doSomething(a, b)
});
const UseCallBackComponent1 = () => {
const name1 = () => "soo";
const name2 = () => "soo";
console.log("name1 : ", name1);
console.log("name2 : ", name2);
return <div>{name1 === name2 ? "같다" : "다르다"}</div>
};
export default UseCallBackComponent1;
실행결과
함수가 객체라서 두 함수는 서로 다른 값입니다.
UseCallBackComponent2.js
const UseCallBackComponent2 = () => {
const [count, setCount] = useState(0);
const clickHandler = () => {
console.log("count : ", count);
};
return (
<div>
<input
type="number"
value={count}
onChange={ (e) => setCount(e.target.value) }
/>
<button onClick={clickHandler}>숫자 출력</button>
</div>
);
};
export default UseCallBackCompoenent2;
const UseCallBackComponent2 = () => {
const [count, setCount] = useState(0);
const clickHandler = () => {
console.log("count : ", count);
};
useEffect(() => { // 👈 추가
console.log("렌더링 완료");
});
return (
<div>
<input
type="number"
value={count}
onChange={ (e) => setCount(e.target.value) }
/>
<button onClick={clickHandler}>숫자 출력</button>
</div>
);
};
export default UseCallBackCompoenent2;
const UseCallBackComponent2 = () => {
const [count, setCount] = useState(0);
const clickHandler = () => {
console.log("count : ", count);
};
useEffect(() => { // 👈 추가
console.log("clickHandler() 변경");
}, [clickHandler]);
return (
<div>
<input
type="number"
value={count}
onChange={ (e) => setCount(e.target.value) }
/>
<button onClick={clickHandler}>숫자 출력</button>
</div>
);
};
export default UseCallBackCompoenent2;
실행결과
UseCallBackComponent2.js
const UseCallBackComponent2 = () => {
const [count, setCount] = useState(0);
const clickHandler = useCallback(() => { // 👈
console.log("count : ", count)
}, [] );
useEffect(() => {
console.log("clickHandler() 변경");
}, [clickHandler]);
// 생략
};
export default UseCallBackComponent2;
실행결과
const UseCallBackComponent2 = () => {
const [count, setCount] = useState(0);
const clickHandler = useCallback(() => {
console.log("count : ", count)
}, [count]);
useEffect(() => {
console.log("clickHandler() 변경");
}, [clickHandler]);
// 생략
};
export default UseCallBackComponent2;
function App() {
const [count, setCount] = useState(0);
const updateHandler = () => {
console.log(`update`)
};
return (
<div>
<input
type="number"
onChange={(e) => setCount(e.target.value)}
/>
<ChildComponent update={updateHandler} />
</div>
)
}
export default App;
const ChildComponent = (props) => {
const { update } = props;
console.log("child component 렌더링");
return <div></div>;
};
export default ChildComponenet;
function App() {
const [count, setCount] = useState(0);
const updateHandler = useCallback(() => { // 👈useCallback() 추가
console.log("update");
}, []);
return (
<div>
<input
type="number"
onChange={(e) => setCount(e.target.value)}
/>
<ChildComponent update={updateHandler} /> // 👈 사용
</div>
)
}
export default App;
실행결과
useCallback()을 사용했지만 자식 컴포넌트가 호출 되는 이유?
const ChildComponent = (props) => { // ❗❗ 실행 될 때마다 새로운 props객체 생성❗❗
const { update } = props;
console.log("child component 렌더링");
return <div></div>;
};
export defualt ChildComponent;
-ChildComponent.js
import React, { memo } from "react"; // 👈
const ChildComponent = (props) => {
const { update } = props;
console.log("child component 렌더링");
return <div></div>;
};
export default memo(ChildComponent); // 👈