반응형

framer-motion이라는 라이브러리를 이용하여 간단한 애니메이션을 구현 할 수 있다.

아래는 framer-motion을 설치하는 방법의 관련된 URL 입니다.

 

https://www.npmjs.com/package/framer-motion

 

framer-motion

A simple and powerful JavaScript animation library. Latest version: 11.0.5, last published: 7 days ago. Start using framer-motion in your project by running `npm i framer-motion`. There are 3777 other projects in the npm registry using framer-motion.

www.npmjs.com

 

framer-motion을 사용하면 초기시점 , 애니메이션 , 끝났을 때(사라질 때)를 선택하여 애니메이션을 만들 수 있습니다.

아래와 같이 지정하면 아코디언을 만들 수 있습니다.

 

주의 사항

꼭 변동이 되는 부분의 <AnimatePresence>로 감싸주어야지만 사라질 때 (exit) 동작 기능이 됩니다.

반응형

※ Debounce이란?

- 이벤트를 호출 시 연속적으로 여러번 호출 되는 것이 아닌 일정한 시간 동안 지연 시간을 주어 반복 횟수를 제어하는 것 입니다.

 

import { useRef, useState } from 'react';

export const useDebounce = (value) => {
	const [DebouncedVal, setDebouncedVal] = useState(value);
	const eventBlocker = useRef(null);

	//Timeout을 초기화 시켜줍니다.
	clearTimeout(eventBlocker.current);

	//일정 시간 동안 지연을 시켜줍니다.
	eventBlocker.current = setTimeout(() => {
		setDebouncedVal(value);
	}, 500);

	return DebouncedVal;
};

 

 

 

반응형

※ uncontrolled input이라는 에러 문구

    - 해당 value값에 값이 없을 때에 대한 대비책이 없을 때 뜨는 오류 문구
    - 해결방법 : 값이 없을 때 빈 문자를 대신 적용

 

<input type='text' value={UserName || ''} />

 

반응형

※ 커스텀 훅 정리

- 커스텀 훅 : 리액트의 기본 훅을 활용해서 자주쓰는 기능들을 마치 플러그인처럼 하나의 패키지로 묶어서 재활용하는 형태
- 커스텀 훅 조건 : 
    1. 파일이름이 무조건 use로 시작 해야 됨
    2. 다른 리액트 hook안쪽에서는 호출이 불가능 useEffect
    3. 다른 핸들러 함수 안쪽에서도 호출이 불가능

- useQuery : 문자열로 구성이 고유의 쿼리키라는 것을 이용해서 비동기를 데이터를 가져와서 관리하기 위한 함수
 고유쿼리키(배열), 데이터패칭함수 , 리액트 쿼리 설정값 (객체) 
 비동기 데이터를 패칭해서 리액트 쿼리 설정값에 따라 캐싱처리한 후 리턴
 
- useMutation : 데이터를 가져오는 것 뿐만 아니라 서버의 데이터를 직접 변경 요청 할 수 있는 함수
- useQueryClient : 추가적인 인스턴스의 함수 호출하기 위한 객체

반응형

※ React 18 버전에서의 Suspense

  - 각 페이지에 구성되 있는 컴포넌트들을 동시에 호출하는 것이 아닌 영역별로 렌더링 시점을 동기화 처리
  - 이전 버전까지는 클라이언트 컴포넌트에서만 제한적으로 동작되는 기술이었으나 18버전부터는 ssr 방식의 컴포넌트에서도 활용 가능하도록 개선

활용 예제)
  1. 특정 컴포넌트가 렌더링 완료 될 때까지 다른 컴포넌트의 렌더링을 막고 이전 렌더링 컴포넌트 완료 후 동기적으로 렌더링 시작
  2. 서버로부터 무거운 데이터를 fecthing하는 컴포넌트의 경우 해당 컴포넌트 출력전까지 자동으로 로딩바 출력

 

 

suspense를 활용 하기 위한 조건

- suspense 동기화 시키는 컴포넌트 내부에 promise 객체 생성 상태(pending, fullfilled , rejected)를 추적 할 수 있어야 됨.

 

 

예제)  useGetData.js

import { useState, useEffect } from 'react';
import axios from 'axios';

//promise 객체를 인수로 받아서 해당 promise 상태에 따라 반환되는 값을 직접 리턴해주는 함수를 반환
const checkPromiseStatus = (promise) => {
	let status = 'pending';
	let result;

	//promise의 상태에 따라 현재 상태값과 반환값을 각각 status, result 변수에 담아줌
	const setPromise = promise.then(
		(value) => {
			status = 'success';
			result = value;
		},
		(error) => {
			status = 'error';
			result = error;
		}
	);

	//위에서 저장되는 status값에 따라 fetching된 결과값을 반환하는 함수를 리턴
	return () => {
		switch (status) {
			case 'pending':
				throw setPromise;
			case 'success':
				return result;
			case 'error':
				throw result;
			default:
				throw new Error('Unknow Status');
		}
	};
};

function useGetData(url) {
	const [Data, setData] = useState(null);

	useEffect(() => {
		const getData = async () => {
			//데이터 요청 후 현재 데이터 상태를 확인하는 promise 객체 자체를 비동적으로 받음
			const promise = axios.get('https://jsonplaceholder.typicode.com/posts').then((response) => response.data);

			//해당 promise 객체를 checkPromiseStatus 함수의 인수로 전달해서 직접 동기화 시키는 커스텀함수 호출후 결과값을 반환값을 state에 담아줌
			setData(checkPromiseStatus(promise));
		};

		getData();
	}, [url]);

	//state에 담아진 promise 반환 값을 리턴
	return Data;
}

export default useGetData;

 

Posts.js

import useGetData from './useGetData';

function Posts() {
	const data = useGetData('https://jsonplaceholder.typicode.com/posts');
	console.log(data);
	return (
		<div>
			{data &&
				data.map((post) => {
					return (
						<div key={post.id}>
							<h2>{post.title}</h2>
							<br />
							<p>{post.body}</p>
							<hr />
						</div>
					);
				})}
		</div>
	);
}

export default Posts;

 

App.jsx

import Posts from './Posts';
import { Suspense } from 'react';

function App() {
	return (
		<div className='App'>
			<h1>App</h1>
			<Suspense fallback={<p>Posts loadding...</p>}>
				<h1>Posts</h1>
				<Posts />
			</Suspense>
		</div>
	);
}

export default App;
반응형

※ 기존의 CSR , SSR 방식 차이

   SSR 작업 흐름

     1. 정적인 HTML 파일을 서버로부터 가져옴
     2. 추후 동적 데이터가 필요할때마다 다시 서버쪽에 요청해서 전체 화면을 full load (화면 깜빡임)
     3. 이후 ajax라는 비동기 서버 통신 기술이 생기면서 전체 화면을 다시 full load 하지 않고 필요한 데이터만 실시간으로 다시 호출 가능
     4. 비동기 데이터를 이용해 자바스크립트로 일일이 동적 DOM을 생성하고 관리해야되는 번거로움이 생김


  CSR 작업 흐름

     1. 빈 HTML파일을 서버로부터 가져옴
     2. 자바스크립트 파일로드 (React)
     3. 리액트 컴포넌트 로드 (Data fetching)
     4. 컴포넌트 해석 후 랜더링 시작
     5. 최종화면에 동적 DOM 생성 (이전 단계까지는 빈 화면 렌더링)


※ React 18 버전에서의 SSR 작업 흐름

   1. 서버쪽에서 미리 static 프리랜더링 된 html파일 로드
   2. 미리 렌더링 된 정적인 화면을 바로 생성 (정적 화면 생성)
   3. 자바스크립트 파일 로드
   4. 동적데이터를 다루는 리액트 컴포넌트 해석
   5. 기존 정적인 화면에 동적으로 연동될 부분만 대체 (hydration) Suspense 활용

반응형

※ useTransition
  - 컴포넌트 렌더링시 연산의 우선순위를 둬서 좀 늦게 렌더링해도 될 것들을 지정
  - 기존에는 한번 렌더링 연산이 시작되면 중간에 멈출 수 없었음
  - 특정 핸들러함수에 의해서 화면을 재 연산해야 되는 경우 중간에 무거운 로직이 실행되는 연산이 있으면 나머지 연산도 같이 지연이 일어남

 

아래는 useTransition 예제 및 설명에 따른 주석 정리 입니다.

import { useState, useTransition } from 'react';

function App() {
	const [Count, setCount] = useState(0);
	const [Items, setItems] = useState([]);
	const [isPending, startTransition] = useTransition();
	//isPending에 넣어두면 늦게 실행 됨.

	console.log('isPending', isPending);

	//아래함수에서는 덜 중요하고 무거운 연산 때문에 급한 연산까지 덩달아 늦게 화면에 랜더링
	const handleClick = () => {
		//urgent op (급하게 처리되야될 중요한 연산)
		setCount(Count + 1);

		startTransition(() => {
			//not urgent op (우선순위가 떨어지는 덜 중요한 연산)
			const arr = Array(20000)
				.fill(1)
				.map((_, idx) => Count + idx);
			setItems(arr);
		});
	};

	//아래 버튼 클릭시 마다 만개의 배열 리스트가 출력되기 전까지는 버튼의 숫자값이 늦게 카운트 되는 것을 확인
	return (
		<>
			<div className='App'>     
        {/* disabled에 isPending을 넣어서 처리가 다 되지 않았을 경우 버튼 선택이 되지 않게 처리 */}
				<button onClick={handleClick} disabled={isPending}>
					{Count}
				</button>
			</div>

			<ul>
				{Items.map((num) => {
					return <li key={num}>{num}</li>;
				})}
			</ul>
		</>
	);
}
반응형

※ Automatic Batching (17버전)
  - 여러개의 state 값이 하나의 핸들러 함수 안쪽에서 동시에 변경이 될 때 그룹으로 묶어서 한번만 렌더링 처리
  - 17에도 동작되는 기능이긴 하나 promise를 반환하는 함수 안쪽에 여러개의 state값이 변경될 경우에는 동작 안됨 

※ Automatic Batching (18버전)
  - 핸들러함수 안쪽에서 복수개의 state값이 변경될때 해당 변경사항을 묶어서(batching)해서 한번만 리랜더링
  - 기존 17버전에서는 promise를 반환하는 함수 안에서는 auto batching동작 불가 (18버전에서 개선됨)

  - 리액트 18에서 동일 핸들러함수 안쪽에 있더라도 flushSync를 이용해 특정 State만 auto batching에서 제외 처리 가능

 

같은 소스임에도 출력되는 내용이 달라짐

function App() {
	console.log('rendered');
	const [Count, setCount] = useState(0);
	const [Count2, setCount2] = useState(0);

	const returnPromise = () => {
		return new Promise((res) => setTimeout(res, 500));
	};

	const handleClick = () => {
		returnPromise().then(() => {
			setCount(Count + 1);
			setCount2(Count2 + 1);
		});
	};

	return (
		<div className='App'>
			<button onClick={handleClick}>button</button>
			<h1>
				{Count}-{Count2}
			</h1>
		</div>
	);
}

export default App;

 

17버전

 

18버전

+ Recent posts