반응형

react 강의를 학습하면서 최근에 제일 많은 차이점이 발생한 것이 router-dom으로 보였다. 5버전과 6버전간에 버전 차이도 꽤 크고 사용법이나 사라진 컴포넌트 함수등이 발생해서 차이점을 알아보고자 한다.

 

React Router Dom V6

react-router-dom 6.4 설치

npm i react-router-dom@6.4

 

 

BrowseRouter, Routes, Route 기본적인 구조

- Router.tsx

import { BrowserRouter, Routes, Route } from "react-router-dom";
import Header from "./components/Header";
import About from "./screen/About";
import Home from "./screen/Home";

function Router() {
  return (
    <BrowserRouter>
      <Header />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}

export default Router;

v5와 달라진점

1. Switch대신 Routes를 사용한다.

2. Route 컴포넌트 element prop에 컴포넌트 명을 입력한다.

 

 

createBrowserRouter

기존 v5와 비슷하게 생긴 BrowseRouter를 사용하여 라우터 표현을 해도 되지만, 아주 새로운 방식으로 라우터를 작성할 수 있다.

 

Router.tsx

import { createBrowserRouter } from "react-router-dom";
import About from "./screen/About";
import Home from "./screen/Home";
import Root from "./Root";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
    children: [
      {
        path: "",
        element: <Home />,
      },
      {
        path: "about",
        element: <About />,
      },
    ],
  },
]);

export default router;

JSX문법 형태로 사용하지 않는다.

createBrowserRouter 함수 파라미터에 Router를 array형태로 표현한다.

 

index.tsx

import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider } from "react-router-dom";
import Router from "./Router";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <React.StrictMode>
    <RouterProvider router={Router} />
  </React.StrictMode>
);

먼저 RouterProvider를 index.tsx에서 추가해주고 router prop에 생성한 Router 함수를 넣어준다.

 

Root.tsx

import React from "react";
import { Outlet } from "react-router-dom";

function Root() {
  return (
    <div>
      <h1>Root입니다.</h1>
      <Outlet />
    </div>
  );
}

export default Root;

또한 Root에서 자식노드를 보여줄 수 있도록 최상단 부모에서 Outlet을 선언해준다.

Outlet덕에 자식 컴포넌트가 표현되어 Home이 보인다.
About경로도 잘 노출이 된다.

 

반응형
반응형

자주 사용하는 명령어들을 모아두기 위해 포스팅을 진행합니다.

 

typescript for react

타입스크립트 버전으로 리액트 프로젝트가 생성된다. my-app부분에 프로젝트명으로 입력

npx create-react-app my-app --template typescript

 

 

styled-component

태그를 생성하고 css를 꾸밀수 있고, 변수를 활용할 수 있다.(1번은 일반, 2번은 타입스크립트버전 설치)

npm i styled-components // 일반.ver
npm i --save-dev @types/styled-components // typescript.ver

 

 

react-router-dom

BrowserRouter, Routes, Route, Link등의 컴포넌트를 사용하여 설정한 경로에 따라 페이지를 렌더링해주는걸 도와준다.

react route dom이 v6이상으로 올라오면서 사용법이 대체적으로 변경되었다. 

(예를들면 Link to prop에 object형태로 옵션을 줄 수 있었으나 각각 prop형태로 적용하도록 변경되었고, 기존에 Switch의 컴포넌트 대신 Routes로 변경되었다.)

npm i react-router-dom

 

 

react-query

useQuery를 사용하여 fetch함수등을 사용하여 api로 데이터를 요청하고 받아오던 행위들을 모아놓고 모듈화를 도와준다.

또한 데이터를 캐싱시켜서 페이지 로드시 속도 개선에도 도움을 준다.

npm i @tanstack/react-query

 

 

react-helmet

사용하면 어디서든 해당 페이지 document의 head태그로 접근 할 수 있다.

title이나 favicon, css, meta태그등 다양하게 접근하고 변경할 수 있도록 도와준다.

npm i --save-dev @types/react-helmet

 

 

recoil

Reactstate management 라이브러리로 페이스북에서 만들었다.

redux와 비슷한 개념으로 사용이 간편하고 쉽다 atom을 사용하여 어디서든 state에 접근하고 사용할 수 있으며, atom의 데이터를 원하는 형태로 변형하여 읽기전용 느낌의 seletor를 사용할 수도 있다.

npm install recoil

 

 

react-hook-form

form태그와 input, select등 form입력의 다양한 키입력 온클릭등의 이벤트 처리와 유효성을 도와준다.

npm install react-hook-form

 

 

react-beatiful-dnd

React프로젝트에서 드래그 앤 드랍처리를 도와주는 라이브러리다.

DragDropContext와 Droppable, Draggable 영역에 대한 이해와 magic(provider), snapshot의 대한 공부를 하고 작성하면 생각보다 쉽게 이벤트 처리를 할 수 있고, 드래그중 드래그 완료 후 각각의 요소별로 css등의 처리를 할 수 있다.

npm i react-beautiful-dnd // 일반.ver
npm i --save-dev @types/react-beautiful-dnd // typescript.ver

 

 

framer-motion

react페이지의 인터렉티브한 효과를 도와주는 애니메이션 라이브러리입니다.

간단한 prop이나 layout속성 추가만으로 컴포넌트간의 애니메이션을 연결해주거나 처리해줄수 있습니다.

npm install framer-motion

 

gh-pages

작성한 리액트 프로젝트를 빌드하여 자신의 github로 배포하도록 도와준다.

npm i gh-pages

 

 

반응형
반응형

React는 state의 값의 변화에 따라 UI가 변화하는 특징을 가지고 있다.

그렇다보니 데이터를 가지고 놀때 useState를 통해 값을 처리하고 setState를 통해 변경하고 변경된 UI부분만 재 처리하는 작업을 반복하는데, 해당값을 상위의 컴포넌트부터 최하단의 컴포넌트까지 복잡한 구조를 props를 통해 넘겨야한다면 굉장히 불필요한 데이터 전달처리로 소스도 난잡해지고 중간 중간의 컴포넌트들은 사용하지도 않는 props데이터를 넘기다보니 처음 소스를 받는사람은 이건 뭔데 넘기고 있을까? 라는 생각을 가질 수도 있을 것이다.

 

이럴때 전역 state처리를 해주는 redux나 recoil이 있는데, recoil에 대하여 알아보고자 한다.

 

Recoil

recoil은 state management 라이브러리로 페이스북팀에서 만들었고, 굉장히 간결하고 강력하다.

실제 예시를 보고 개념도 쉽고 그림도 잘 그려져서 너무 좋았다.

 

설치하기

먼저 사용을 해보기 위해 설치를 해야한다.

npm install recoil

 

설치가 끝나면 recoil설정 파일이 필요하다.

recoil에서 특정 전역처리를 하는 영역 덩어리를 atom이라고 부른다.

atoms.ts파일을 열어주고

전역 state를 처리할 값을 입력해주고 필요한 부분들에서 불러서 사용하면 된다.

 

그 전에 전체를 RecoilRoot로 App을 감싸주는걸 잊지 말자!

 

-atom

atoms.ts

import { atom } from "recoil";

export const isDarkAtom = atom({
    key: "isDark",
    default: true,
})

atom에는 key와 default값을 object형태로 넣어준다.

key값은 global state로 사용할 key값이고, default는 기본 값이다. 기본값으로 인해 자료형도 선택이 된다.

 

- useRecoilValue

전역 state값 불러오기

파라미터에는 호출할 atom을 넣어줍니다.

import { isDarkAtom } from "../atoms";
import { useRecoilValue } from "recoil";

const isDark = useRecoilValue(isDarkAtom);

 

- useSetRecoilState

전역 state값 변경하기

파라미터에는 호출할 atom을 넣어줍니다.

import { isDarkAtom } from "../atoms";
import { useSetRecoilState } from "recoil";

const setIsDark = useSetRecoilState(isDarkAtom);
setIsDark(true); //atom의 state값 변경

 

- 값불러오고 변경하기를 useState처럼 처리하기

파라미터에는 호출할 atom을 넣어줍니다.

import { useRecoilState } from "recoil";
import { isDarkAtom } from "../atoms";

const [isDark, setIsDark] = useRecoilState(isDarkAtom);
console.log(isDark);
setIsDark(true);

많이 보던 형태가 아닐까 싶습니다.

바로 useState랑 생김새가 똑같습니다.

값만 불러야하거나 변경만 해야하면 위에서처럼 하나만 import해서 쓰면 되지만, 둘다 필요한 경우에는 해당 방식이 좋아보입니다. :)

 

 

반응형
반응형

react-hook-form을 사용하여 form태그 내의 input태그의 onChange이벤트, 유효성 검사, submit후 데이터 값 확인 등 다양한 작업을 손쉽게 처리 할 수 있습니다.

 

기존의 react를 사용하여 해당 기능을 처리하려면 useState를 통해 처리할 값고 set함수를 선언하고 onChange이벤트에 넣어주고  value값에 state값을 넣어주고, 유효성을 체크하려면 onChange에 걸어둔 함수에서 길이를 체크하거나 정규식으로 패턴을 검사하거나 한개의 input만 해도 아주 복잡한 코드가 만들어지는데, 복잡한 회원가입이나 입력폼에서 이 과정을 실행하려면 상당한 긴 소스가 작성될 것입니다. 이런 문제를 간단하게 해결해 줄 수 있습니다.

 

먼저 설치를 진행합니다.

npm install react-hook-form

 

기본적으로 사용을 위해서 useForm이라는 hook을 항상 import해서 사용합니다.

import { useForm } from "react-hook-form"

 

1. register

register는 input 기능에 특화되어 있는 함수로 onBlur, onChange를 가지고 있는 함수입니다.

처리하고자 하는 input태그 내에 {...register("name")} 하나만으로 모든 처리가 끝납니다.

 

-사용 방법

import { useForm } from "react-hook-form";

interface IForm{
    userId: string;
    email: string;
}

function App(){
    const { register } = useForm();

    return (
        <div>
            <form>
                <input {...register("userId")} placeholder="아이디" />
                <input {...register("email")} placeholder="이메일" />
                <button>추가</button>
            </form>
        </div>
    )
}

export default App;

단지 jsx input 내부에 {...register("이름")}만으로 onChange이벤트에 의해 value값이 처리되는 걸 볼 수있습니다.

 

-유효성 검사

register함수의 2번째 파라미터부터 유효성 검사를 할 요소를 object형태로 넣어주면 됩니다.

{
    required: "아이디를 입력하세요.",
    minLength: {
        value: 5,
        message: "아이디는 최소 5자리 이상입니다."
    },
    maxLength: {
        value: 12,
        message: "아이디는 최대 12자리 이상입니다."
    }    
}

required: true, minLength:5, maxLength: 12 이런식으로 넣어줘도 되지만, 오류메시지 출력을 위해 value값과 message로 나눠서 처리해주었습니다.

 

-유효성 검사 예시

import { useForm } from "react-hook-form";

interface IForm{
    userId: string;
    email: string;
}

function App(){
    const { register } = useForm();

    return (
        <div>
            <form>
                <input {
                    ...register("userId",
                    {
                        required: "아이디를 입력하세요.",
                        minLength: {
                            value: 5,
                            message: "아이디는 최소 5자리 이상입니다."
                        },
                        maxLength: {
                            value: 12,
                            message: "아이디는 최대 12자리 이상입니다."
                        },
                    }
                )} placeholder="아이디" />
                <input {...register("email")} placeholder="이메일" />
                <button>추가</button>
            </form>
        </div>
    )
}

export default App;

 

-pattern(정규식 체크)

<input {
    ...register("email", {
        required: "이메일을 입력하세요.",
        pattern: {
            value: /^[a-zA-Z0-9+-\_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/,
            message: "이메일 형식이 아닙니다."
        },
})} placeholder="이메일" />

 

-validate옵션을 통한 커스텀을 통한 유효성 검사

validate: {
    noAdmin: (value) => value.includes("admin") ? "admin은 포함 시킬 수 없습니다." : true,
    noShin: (value) => value.includes("Shin") ? "Shin은 포함 시킬 수 없습니다." : true,
}

validate의 return값이 false 또는 문자열이 발생하면 유효하지 않는 값으로 인식하기 때문에, 오류로 처리하고자 하는 값이면 false처리하거나 메시지를 입력하면 되고 유효한 데이터라면 true를 처리해주면 됩니다.

 

 

2. watch

form태그의 입력된 값들의 변화를 관찰해주는 함수입니다.

userForm에서 watch함수를 호출하기만 하면 값이 변경될 때 마다 동작하는 것을 확인 할 수 있습니다.

const { register, watch } = useForm<IForm>();

console.log(watch());

 

값을 입력할때마다 console.log에 와치함수가 동작하여 태그 값들이 찍히고 있다.

 

 

3. handleSubmit

onSubmit의 동작을 처리해줄 handleSubmit입니다.

handleSubmit에는 2개의 인자를 받습니다.

onValid와 onInvalid

타입스크립트 설명에서 알 수 있듯이 첫번째 파라미터는 필수값이며, 유효한 경우 동작할 함수를 넣어줍니다.

두번째 파라미터는 선택이며, 유효하지 않은 경우 동작할 함수를 넣어주면 됩니다.

 

사용법은 form태그 onSubmit에 해당 함수를 넣어주면 됩니다.

import { useForm } from "react-hook-form";

interface IForm{
    userId: string;
    email: string;
}

function App(){
    const { register, handleSubmit } = useForm<IForm>();

    const onValid = (data:IForm) => {
        console.log(data);
    }

    return (
        <div>
            <form onSubmit={handleSubmit(onValid)}>
            </form>
        </div>
    )
}

export default App;

onValid라는 함수를 만들었습니다.

IForm interface의 정의를 받는 타입의 object값들을 받아서 console.log에 찍어주는 함수를 생성하였습니다.

form태그 내부에는 onSubmit태그 내의 handleSubmit함수를 생성하였고 첫번째 인자에 onValid를 넣어주었습니다.

 

 

4. formState:erros

useForm내의 formState를 사용하면 유효하지 않는 값들을 걸러내고 어떤 오류의 종류인지 오류메시지는 무엇인지 알 수 있습니다.

formState를 호출하고 formState의 errors메시지를 호출하는 것으로 에러들을 체크 할 수 있습니다.

const { register, handleSubmit, formState } = useForm<IForm>();
console.log(formState.errors);

이것들을 각 항목별로 유효성 오류를 표출시킬 수 있습니다.

사진에서 볼 수 있듯이 register에서 등록한 이름별로 오류메시지들이 출력됩니다.

type에는 어떤 종류인지 나옵니다.

message에는 문자열로 정의한 오류 메시지가 출력됩니다.

해당 정보들을 토대로 아래처럼 각 항목 아래에 오류 메시지를 출력시킬 수 있습니다.

 

errors?.항목이름?.message

오류 데이터가 없는 경우가 있을 수 있으니 꼭 optional(?.)처리를 해주세요.

 

-오류 표출해보기

import { useForm } from "react-hook-form";

interface IForm{
    address: string;
    email: string;
    nickName: string;
    phone: string;
    userId: string;
    userPw: string;
    userPwConfirm: string;
}

function Join(){
    const { register, watch, handleSubmit, formState:{errors} } = useForm<IForm>();

    //handleSubmit은 2개의 인자를 받는데 첫번째는 유효한 데이터인 경우 호출되는 함수다.
    //두번째 인자는 유효하지 않는 경우 호출되는 함수로 첫번째 함수만 필수값이다.
    const onValid = (data:IForm) => {
        console.log(data);
    }
    
    //에러체크는 formState의 error함수를 사용하여 알 수 있다.
    console.log(errors);
    return (
        <div>
            <form 
                style={{display:"flex", flexDirection:"column"}} 
                onSubmit={handleSubmit(onValid)} >
                <input {...register("userId", {required: "회원 아이디를 입력하세요."})} placeholder="아이디"/>
                <span>{errors?.userId?.message}</span>
                <input {...register("userPw", {required: "비밀번호를 입력하세요.", minLength: 10})} placeholder="비밀번호"/>
                <span>{errors?.userPw?.message}</span>
                <input {...register("userPwConfirm", {required: "비밀번호 확인을 입력하세요.", minLength: 10})} placeholder="비밀번호확인"/>
                <span>{errors?.userPwConfirm?.message}</span>
                <input {...register("nickName", {required: "닉네임을 입력하세요.", minLength: 10})} placeholder="닉네임"/>
                <span>{errors?.nickName?.message}</span>
                <input {
                    ...register("email", {
                        required: "이메일을 입력하세요.",
                        pattern: {
                            value: /^[a-zA-Z0-9+-\_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/,
                            message: "이메일 형식이 아닙니다."
                        },
                    })}
                    placeholder="이메일"/>
                <span>{errors?.email?.message}</span>
                <input {...register("address", {required: "주소를 입력하세요."})} placeholder="주소"/>
                <span>{errors?.address?.message}</span>
                <input {...register("phone", {required: "전화번호를 입력하세요."})} placeholder="전화번호"/>
                <span>{errors?.phone?.message}</span>
                <button>회원가입</button>
            </form>
        </div>
    );
}

export default Join;

작성 후 회원가입 버튼을 눌러서 submit동작을 시켜보면 각 오류메시지 타입들이 노출됩니다.

formState의 errors메소들을 주로 많이 사용하기 때문에 아래처럼 바로 가져와서 사용합니다.

const { formState:{errors} } = useForm<IForm>();

 

 

5. defaultValues

defaultValues로 기본값을 처리할 수 있습니다.

useForm의 첫번째 인자에 object형태로 정의해주면 됩니다.

const { register, handleSubmit, formState:{errors} } = useForm<IForm>({
    defaultValues: {
        phone: "010"
    }
});

타입스크립트로 useForm<IForm> 을 처리해둔 덕에 자동완성으로 리스트들이 나옵니다.

defaultValues내부에 원하는 값을 넣고 기본값을 입력해주면 UI에 바로 입력되어서 나오는 걸 볼 수 있습니다.

 

 

6. setError

setError를 통해 강제로 에러를 발생시킬 수도 있습니다.

역시나 타입스크립트가 사용법에 대해서 알려주고 있습니다.

 

첫번째 인자에는 에러를 발생시킬 항목을  두번째에는 에러 옵션을 넣으라고 나옵니다. 세번째는 선택입니다.

 

아래는 비밀번호 확인을 하여 오류가 발생한 경우를 작성했습니다.

const { setError } = useForm<IForm>();

const onValid = (data:IForm) => {
    if(data.userPw !== data.userPwConfirm){
        setError("userPwConfirm", 
                    {message: "비밀번호가 일치하지 않습니다."},
                    {shouldFocus: true}
            );
    }
}

submit동작을 해보면 오류가 발생하는 것을 볼 수 있습니다.

캡처에서는 빨간 밑줄이 그어져있지만 바로 포커싱을 가게 하고 싶다면 shouldFocus옵션을 true로 주면 된다.

 

 

 

또한 onValid나 InValid쪽에서 서버 오류등이 발생한 경우에도 강제로 오류를 발생시켜주면 좋을텐데 이런 경우에도 사용할 수 있습니다.

 

예외 처리에 대한 값이 필요한데 타입스크립트이므로 interface에 form태그에 대한 값을 추가하고, jsx에서 오류처리를 하던 부분에 동일하게 처리를 해주면 됩니다.

다만 interface에서 해당 extra값은 필수값은 아니므로 '?'처리를 해줍니다.

 

-비밀번호와 비밀번호 확인이 다른 경우 오류 발생처리(Full ver_Join.tsx)  & 강제 오류발생으로 메시지 띄우기 소스

import { useForm } from "react-hook-form";

interface IForm{
    userId: string;
    extraError?: string;
}

function Join(){
    const { 
        register, 
        handleSubmit, 
        formState:{errors},
        setError
    } = useForm<IForm>({
        defaultValues: {
            phone: "010"
        }
    });

    //handleSubmit은 2개의 인자를 받는데 첫번째는 유효한 데이터인 경우 호출되는 함수다.
    //두번째 인자는 유효하지 않는 경우 호출되는 함수로 첫번째 함수만 필수값이다.
    const onValid = (data:IForm) => {
        if(data.userPw !== data.userPwConfirm){
            setError("userPwConfirm", {message: "비밀번호가 일치하지 않습니다."});
        }
        setError("extraError", {message: "알 수 없는 오류가 발생했습니다."});
    }
    
    return (
        <div>
            <form 
                style={{display:"flex", flexDirection:"column"}} 
                onSubmit={handleSubmit(onValid)} >
                <input {...register("userId", {required: "회원 아이디를 입력하세요."})} placeholder="아이디"/>
                <span>{errors?.userId?.message}</span>
                <input {...register("userPw", {required: "비밀번호를 입력하세요.", minLength: 10})} placeholder="비밀번호"/>
                <span>{errors?.userPw?.message}</span>
                <input {...register("userPwConfirm", {required: "비밀번호 확인을 입력하세요.", minLength: 10})} placeholder="비밀번호확인"/>
                <span>{errors?.userPwConfirm?.message}</span>
                <input {...register("nickName", {required: "닉네임을 입력하세요.", minLength: 10})} placeholder="닉네임"/>
                <span>{errors?.nickName?.message}</span>
                <input {
                    ...register("email", {
                        required: "이메일을 입력하세요.",
                        pattern: {
                            value: /^[a-zA-Z0-9+-\_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/,
                            message: "이메일 형식이 아닙니다."
                        },
                    })}
                    placeholder="이메일"/>
                <span>{errors?.email?.message}</span>
                <input {...register("address", {required: "주소를 입력하세요."})} placeholder="주소"/>
                <span>{errors?.address?.message}</span>
                <input {...register("phone", {required: "전화번호를 입력하세요."})} placeholder="전화번호"/>
                <span>{errors?.phone?.message}</span>
                <button>회원가입</button>
                <span>{errors?.extraError?.message}</span>
            </form>
        </div>
    );
}

export default Join;

 

submit동작 후 강제 에러가 발생된 것을 볼 수 있습니다.

 

 

7. setValue

setValue를 통해 강제로 값을 변경할 수 있다.

입력 후 특정 값을 비워야하거나 자동 맞춤을 해주거나 할 경우 필요한 기능으로 2개의 필수 파라미터와 세번째는 옵션값을 갖는다.

첫번째 파라미터는 대상(항목)이며, 두번째 파라미터는 처리할 값이다.

처리할 값은 타입스크립트라면 정의해놓은 값으로 넣어주면 된다. string > string, number > number

사용 예시

setValue("toDo", ""); //toDo항목의 값을 비워준다.
반응형
반응형

npm을 통해 리액트 프로젝트를 생성하려고 했는데, 아래와 같이 반복적으로 오류가 발생하고 있었다.

 

업데이트도 안되고 npm의 버전조차 나오지 않았다.

(여러 강의나 다른글의 모듈등을 설치하면서 버전간에 문제가 있게되었고 설정이 꼬인거 같았다...😣)

 

해결방법

1. npm 재설정 처리를 위해 다음과 같이 기존 캐시 파일들을 삭제하였다.

C:/Users/계정명/AppData/Roaming/ 이동

npm, npm-cache 디렉토리 2개 삭제

 

 

2. 다음 명령어를 입력한다.

npm cache clean

 

해결!😎

정상적으로 설치버전까지 나온다!

 

 

반응형
반응형

늦은감이 있지만 리액트 스터디를 재시작했습니다.

 

언어를 습득하는데 작은 프로젝트라도 해보는게 머리에 잘 남기에 개인 이력이 담긴 포트폴리오를 작성하고 깃허브에 올리는 프로젝트를 수행 중이였습니다.

 

여러가지 최대한 아는 기능선에서 만들어보고 있었는데, 자기 소개를 하는 페이지를 작성하는 부분의 텍스트를 실시간으로 타이핑하는 효과를 주기 위해 이미 작성 된 데이터를 담아둔 배열을 setInterval을 통해 표현하면 될 거라고 생각하고 작성을 시작했습니다.

 

useEffect를 통해 로드 시 한번만 setInterval이 동작하도록 하였고, 생각보다 많은 시행착오를 겪고 나서 처리까지 완료가 되었습니다.

 

아래는 처음 작성해 본 예제입니다.

Introduce.js

import { useState, useEffect } from "react";
import {MdKeyboardReturn} from  "react-icons/md";
import "./Introduce.scss";
import bg_img from "../../../assets/imgs/monet.jpg";

const Introduce = () => {
    const OPTION = {
        writeArr: [],
        arrIndex: 0,
        index: 0
    };
    const WORD_TYPING_SPEED = 80;
    const msgArr = [
        " 안녕하세요? 웹 개발자 박신우입니다.",
        " 저를 소개하기 위한 페이지를 만들었습니다.",
        " 저의 정보를 확인해주세요.",
    ];

    const [introduce, setIntroduce] = useState([]);

    const onChangeMsg = (o) => {
        const msg = msgArr[o.arrIndex];
        o.writeArr[o.arrIndex] = msg.substring(0, ++o.index);
        if(o.index > msg.length) {
            o.index = 0;
            if(++o.arrIndex >= msgArr.length){
                o.arrIndex = 0;
                o.writeArr.splice(0, o.writeArr.length);
            }
        }
        setIntroduce(() => [...o.writeArr]);
    };

    useEffect(()=>{
        const interval = setInterval(()=>{
            onChangeMsg(OPTION);
        }, WORD_TYPING_SPEED);
        return () => clearInterval(interval);
    }, []);
    
    return (
        <div id="introduce" className="Introduce" style={{background: `url(${bg_img}) no-repeat`}}>
            <h2 className="introduce_msg">
                {
                    introduce.map((m, i) => (
                        msgArr[i].length === m.length ? <p key={i}>{m}</p>
                        : <p key={i}>{m}<MdKeyboardReturn /></p>                    
                    ))
                }
            </h2>
        </div>
    );
}

export default Introduce;

나름 UI를 확인해봤을땐 별 문제 없이 잘 동작하는것으로 보였습니다.

 

하지만, 관리자 모드를 열어서 확인해보니 Hook관련 에러가 계속해서 발생하는 것을 확인했습니다.😣

 

처리 방법

검색을 하다보니 setInterval과 react의 동작 구조 개념에서 발생하는 현상이라는 번역 글을 확인했습니다.

 

아래는 제가 참고한 블로그 글입니다.

https://velog.io/@jakeseo_me/%EB%B2%88%EC%97%AD-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85%EC%8A%A4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%97%90%EC%84%9C-setInterval-%EC%82%AC%EC%9A%A9-%EC%8B%9C%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90

 

번역 / 리액트 훅스 컴포넌트에서 setInterval 사용 시의 문제점

Dan abramov의 https://overreacted.io/making-setinterval-declarative-with-react-hooks/ 번역입니다.All copyrights to Dan Abramovtranslated by Jake seoTHE

velog.io

 

setInterval대신 커스텀으로 작성된 컴포넌트 useInterval을 사용하라는 간단한 제안이 되어있습니다.

 

별도로 컴포넌트를 작성후 import하여 해결하였고, 그 결과는 아래 소스입니다.

useInterval.js

import { useEffect, useRef } from 'react';

const useInterval = (callback, delay) => {
  const savedCallback = useRef();

  // Remember the latest callback.
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // Set up the interval.
  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      let id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

export default useInterval;

 

Introduce.js

import { useState } from "react";
import useInterval from "../../common/useInterval";
import {MdKeyboardReturn} from  "react-icons/md";
import "./Introduce.scss";
import bg_img from "../../../assets/imgs/monet.jpg";


const OPTION = {
    writeArr: [],
    arrIndex: 0,
    index: 0
};
const WORD_TYPING_SPEED = 80;
const msgArr = [
    " 안녕하세요? 웹 개발자 박신우입니다.",
    " 저를 소개하기 위한 페이지를 만들었습니다.",
    " 저의 정보를 확인해주세요.",
];

const Introduce = () => {
    const [introduce, setIntroduce] = useState([]);

    const onChangeMsg = (o) => {
        const msg = msgArr[o.arrIndex];
        o.writeArr[o.arrIndex] = msg.substring(0, ++o.index);
        if(o.index > msg.length) {
            o.index = 0;
            if(++o.arrIndex >= msgArr.length){
                o.arrIndex = 0;
                o.writeArr.splice(0, o.writeArr.length);
            }
        }
        setIntroduce(() => [...o.writeArr]);
    };

    useInterval(() => {
        onChangeMsg(OPTION);
    }, WORD_TYPING_SPEED);

    return (
        <div id="introduce" className="Introduce" style={{background: `url(${bg_img}) no-repeat`}}>
            <h2 className="introduce_msg">
                {
                    introduce.map((m, i) => (
                        msgArr[i].length === m.length ? <p key={i}>{m}</p>
                        : <p key={i}>{m}<MdKeyboardReturn /></p>                    
                    ))
                }
            </h2>
        </div>
    );
}

export default Introduce;

소스도 한결 가벼워지고 더 이상 오류문구도 발생하지 않는걸 볼 수 있습니다.

useInterval에서 useEffect를 통해 setInterval을 처리하기 때문에 useEffect로 감싸지 않는것을 볼 수 있습니다.

반응형
반응형

리액트에서 조건문 사용하는방법을 알아보겠습니다.

 

리액트에서는 클래스로 만들어진 컴포넌트에 꼭 render함수가 있어야하며 JSX로 return을 해줘야하는데,

JSX에서는 자바스크립트 조건문처럼 if나 switch문으로 처리가 불가능합니다.

 

틀린 문법 - IF문

import React from 'react';

function App() {
  const test = 12345;
  return (
    if(test === 12345){
      <div>맞아요</div>
    }else{
      <div>틀려요</div>
    }
  );
}

export default App;

 

해당방식으로 완성하게되면, 구문에러를 확인 할 수 있습니다.

파싱 에러

 

리액트 JSX에서는 삼항연사자 또는 AND(&&) 연산자를 통해 조건문을 구성합니다.

 

 

삼항연사자로 조건확인하기

import React from 'react';

function App() {
  const test = 12345;
  return (
    test === 12345 ? <div>맞아요</div> : <div>틀려요</div>
  );
}

export default App;

test 상수값이 12345인 경우 참
test 상수값이 12345가 아닌경우 거짓

이와 같이 if else처리를 삼항연산자를 통해 만드시면 됩니다.

 

 

AND연산자를 통한 처리

import React from 'react';

function App() {
  const test = 12345;
  return (
    test === 12345 && <div>맞아요 (AND연산)</div>
  );
}

export default App;

AND연산을 통한 조건문

AND연산은 조건문이 true일때만 동작하는 else문이 없는 조건문이라고 생각하면 됩니다.

 

 

간단한 조건문의 경우 위의 삼항연산자나 AND연산자를 통해 완성시킬 수 있지만 복잡한 경우 어떻게 해야할까요?

삼항에 삼항에 삼항은 가독성도 떨어지고 만들기도 힘든데 이때는 즉시발동함수를 사용하시면 됩니다.

 

즉시발동함수를 통한 조건문 처리

import React from 'react';

function App() {
  const word = 'react';
  return (
    <div>
    {
      (function(){
        if(word == 'react'){
          return ("리액트!")
        }else if(word == 'hello'){
          return ("헬로우!")
        }else if(word == 'world'){
          return ("월드!")
        }
      })()
    }
    </div>
  );
}

export default App;

즉시발동함수로 조건문 처리

 

사실 조건에 해당하면 바로 return처리되기 때문에 else if도 필요없긴 합니다...

 

화살표함수 형태로 표현도 가능합니다. 화살표함수는 자바 1.8이상에서 보는 list-> 와 비슷한 함수로

es6 문법에서 나온 함수로 function()부분을 대체합니다.

 

즉시발동함수(화살표함수)를 통한 조건문 처리

return (
    <div>
    {
      (()=>{
        if(word == 'react'){
          return ("리액트!")

function()부분을 ()=> 변경하여도 동일하게 동작합니다.

 

이상으로 리액트 조건문 사용방법을 알아봤습니다.

반응형