what is Effect?
리액트의 main job이 아닌 모든 것을 effct 또는 side effect 라고 부른다.
- main | 리액트는 UI를 설계하는 자바스크립트 라이브러리이다.
- props을 이용해서 UI를 rendering 한다 => 재사용 가능한 component
- 사용자의 input에 따라 state를 관리하고, UI를 re-rendering 한다 => reacting to user
- side | 리액트의 main job이 아닌 모든 것
- http request
- 타이머 설정하기
- 브라우저 storage에 데이터 저장하기
- response에 따라서 어떤 데이터를 저장하고 처리하기(사용자의 input을 저장하고, 유효성을 체크하는 것도 side effect에 해당한다).
what is useEffect?
useEffect는 리액트의 모든 side job(side effect)를 처리하는 함수이다. 컴포넌트 안에서 useEffect를 사용하지 않고, side job을 실행하는 코드가 있다면 버그나 무한 루프에 빠질 가능성이 있다. 예를 들어 컴포넌트 안에서 직접 localStorage에 접근 해서 이전에 로그인한 기록이 있는지를 확인하고, 그 결과에 따라 로그인 state를 업데이트하는 코드가 있다고 해보자.
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
if(localStorage.getItem(isLoggedIn) === '1') {
setIsLoggedIn(true);
}
// ...
}
- App이 실행되면 이전에 로그인 한 기록이 있는지 체크하고, 있다면 state를 업데이트 한다.
- state가 업데이트 되면, 컴포넌트 함수를 재실행한다.
- 로그인 한 기록이 있는지 확인하고 state를 업데이트 한다. => 무한 루프
이런 상황을 핸들링하기 위해 사용할 수 있는 것이 리액트에서 제공하는 useEffect 함수이다.
how to use useEffect?
no dependencies
// useEffect(( )=>{ }, [ dependencies ]);
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
useEffect(() => {
if(localStorage.getItem(isLoggedIn) === '1') {
setIsLoggedIn(true);
}
}, [])
const loginHandler = () => {
localStorage.setItem('isLoggedIn', '1');
setIsLoggedIn(true);
};
const logoutHandler = () => {
localStorage.removeItem('isLoggedIn');
// or localStorage.setItem('isLoggedIn', '0');
setIsLoggedIn(false);
};
// ...
}
- App 컴포넌트 함수가 전부 실행된다.
- useEffect에 전달된 콜백함수로 돌아와서 이를 실행된다.
- 컴포넌트 함수의 바로 아래에서 실행되지 않고(not directly) 리액트(useEffect)에 의해 실행된다.
- localStorage에서 데이터를 가져오는 것, 즉 side effect code가 여기에 작성된다.
- 이 콜백에서 state를 업데이트 하고 있으므로 App 컴포넌트 함수를 재실행한다.
- 이번에는 전달된 dependencies의 변경 여부에 따라 useEffect의 콜백의 실행 여부가 결정된다.
- App이 처음 호출 된 것을 dependecies가 변경된 것으로 간주하기 때문에 2번에서 effect가 실행되었다.
- dependencies로 비어있는 배열을 전달하는 것과, 아무것도 전달하지 않는 것은 다르다.
- dependencies에 비어있는 배열을 전달하면 (no dependencies),dependencies가 변경될 일이 없기 때문에 콜백 함수는 어플리케이션이 처음 실행될 때 한 번만 호출된다.
- dependencies에 아무것도 전달하지 않으면, dependencies 자체가 없기 때문에 component가 재실행 될 때마다 콜백 함수가 실행된다. 즉, 무한루프가 발생하는 첫번째 예제 코드와 다름 없어지므로 주의한다.
dependencies
어플리케이션이 처음 시작되고 난 이후에도, 콜백 함수를 실행해야 한다면 dependencies 배열에 아이템을 전달해야 한다. 예를 들어 사용자가 email과 password의 input에 값을 입력할 때마다(onChange 이벤트가 발생할 때 마다) 유효성 검사를 실행하고 그 결과를 저장해야 한다면, 각 input value를 dependencies로 전달할 수 있다.
+) onBlur | input이 포커스를 잃을 때 마다 발생하는 이벤트 => 양식이 유효하지 않은데 blur 이벤트가 발생하면 border의 색을 바꿀 수 있다.
const Login = (props) => {
const [enteredEmail, setEnteredEmail] = useState('');
const [enteredPassword, setEnteredPassword] = useState('');
const [formIsValid, setFormIsValid] = useState(false);
const emailChangeHandler = (event) => {
setEnteredEmail(event.target.value);
};
const passwordChangeHandler = (event) => {
setEnteredPassword(event.target.value);
};
useEffect(() => {
setFormIsValid(
enteredEmail.includes('@') && enteredPassword.trim().length > 6
);
}, [enteredEmail, enteredPassword]);
// ...
}
- 만약 useEffect를 사용하지 않는다면, form 유효성을 설정하는 set 함수를 email과 pw change 핸들러에서 각각 호출해야 한다.
clean up
위에서 작성한 effect 코드가 실행될 때 발생하는 문제점이 있다.
useEffect(() => {
setFormIsValid(
enteredEmail.includes('@') && enteredPassword.trim().length > 6
);
}, [enteredEmail, enteredPassword]
);
- onChange 이벤트가 발생할 때 마다 entered input state가 업데이트 되고있다.
- dependencies로 해당 input을 가지고 있기 때문에, onChange 이벤트가 발생할 때 마다 useEffect의 콜백함수가 실행된다.
- 이 콜백 함수에서는 다시 form의 유효성 state를 업데이트 하고 있다.
- 컴포넌트 함수가 다시 호출된다. => low performance
- 컴포넌트 함수가 다시 호출된다는 것이 곧 re-rendering이 발생한다는 것을 의미하지는 않는다.
- 다만, 컴포넌트 함수가 호출되면 re-rendering을 할 것인지 아닌지를 결정하는 코드가 실행된다.
- 실제로 DOM 요소에 변화가 생겼을 때에만 re-rendering을 하겠다고 결정한다.
- 따라서 사용자가 키를 누를 때 마다 콜백 함수를 실행하기 보다는, 사용자가 키를 누른 후 얼마의 시간 동안 키를 누르지 않고 멈춰있을 때 콜백 함수를 실행하는 것이 더욱 효율적이다(특히 http request와 관련해서). => input debounce(재그룹화)
- 이것을 가능하게 해주는 것이 clean up function이다.
useEffect(() => {
const checkValidation = setTimeout(() => {
console.log("checking form validation"); // 나중에 출력(콜백이 재실행 될 때)
setFormIsValid(
enteredEmail.includes('@') && enteredPassword.trim().length > 6
);
}, 500)
return ()=>{
console.log("executing cleanup function"); // 먼저 출력(콜백이 재실행 될 때)
clearTimeout(checkValidation);
}
}, [enteredEmail, enteredPassword]); // dependency로 input이 아니라 유효성 자체를 전달하는 것이 더 좋다
- useEffect에 전달되는 콜백 함수는 함수를 return 할 수 있다.
- 이 함수를 clean up function이라고 부른다.
- clean up function은 다음 상황에서 실행된다.
- useEffect의 콜백이 재실행 되기 이전에 실행된다(단 useEffect가 처음 실행 될 때에는 실행되지 않는다).
- useEffect를 사용하고 있는 컴포넌트가 DOM에서 un-mount 되기 전(DOM에서 제거 되기 전)에 실행된다. => 이 경우에는 dependencies에 상관 없이 컴포넌트가 언마운트 되기만 하면 실행된다.
- 위의 코드는 다음과 같이 동작한다.
- effect의 콜백 함수가 처음 실행될 때에는 clean up 함수가 호출되지 않는다. 따라서 처음에는 콘솔에 유효성 체크 중이 출력되고 타이머가 실행된다.
- 사용자가 input을 변경해서 콜백함수가 재실행되기 전에, clean up이 호출된다. 여기에서 이전의 timer 함수를 삭제한다.
- effect의 콜백함수가 실행되고 새로운 timer가 생성된다.
- 500ms이 지나기 전에 다시 사용자가 input을 변경하면, 이전의 timer를 삭제하고, 콜백함수는 다시 새로운 timer를 만든다(반복).
input이 변경될 때 마다 유효성 검사를 하지 않아도 된다.
+) clean up 함수를 이용해서 범퍼 애니메이션 만들기
const [bumpClass, setBumpClass] = useState(null);
const buttonClass = bumpClass?
`${props.className} ${styles.cart} ${styles.bump}`
: `${props.className} ${styles.cart} `;
useEffect(() => {
if (context.orderList.length === 0) return;
setBumpClass(true);
const removeBumpClass = setTimeout(() => {
setBumpClass(false);
}, 200);
return () => {
clearTimeout(removeBumpClass);
};
}, [context.orderList]);
// ...
return (
<button className={buttonClass}>
// ...
</button>
);
'Study > React' 카테고리의 다른 글
useContext (0) | 2022.06.20 |
---|---|
useReducer (0) | 2022.06.19 |
useRef (0) | 2022.06.18 |
Fragment & Portal (0) | 2022.06.18 |
Component Styling (0) | 2022.06.18 |