리액트 기초반 - 3

Lipton·2021년 9월 27일
0

항해99

목록 보기
10/21
post-thumbnail

Event Listener

사용자가 어떤 행동(=이벤트)을 하는 지 알려 줌
이벤트 리스너는 <div onClick={}>에서처럼 엘리먼트에 직접 넣어줄 수도 있지만, addEventListener를 통해 추가할 수 있다.
리액트 함수형에서는 useEffect() 리액트 훅을 사용한다.
리액트 훅은 라이프 사이클 함수 중 componentDidMount와 componentDidUpdate, componentWillUnmount를 합쳐둔 것이다.

React.useEffect(() => {}, []);
컴포넌트가 렌더링 되면 화살표 함수를 실행한다.

실행의 조건

  • 첫 렌더링에서는 무조건 실행
  • 두번째 실행부터는(re rendering) 두번째 배열[](의존성 배열)에 들어가 있는 요소를 확인, 요소가 바뀐것이 있을 때 함수 실행
  • 의존성(Dependency) 배열이 비어있으면 이 함수는 다시 실행되지 않음.
  // 두번째 인자의 []! 디펜던시 어레이라고 불러요. 여기 넣어준 값이 변하면 첫번째 인자인 콜백함수를 실행합니다.
  React.useEffect(() => {
    // 여기가 rendering 때 실행될 구문이 들어가는 부분입니다.
    // componentDidMount, componentDidUpdate일 때 동작하는 부분이 여기예요.
    // do something ...

    return () => {
        // 여기가 clean up 부분입니다.
        // componentWillUnmount 때 동작하는 부분이 여기예요.
      //do something ...
    };
  }, []);

예시

import React from "react";

const Text = (props) => {
  const text = React.useRef(null); //<h1>태그 받아 옴

  const hoverEvent = () => {
    text.current.style.background = "yellow";
  } //배경색 노랑으로 할 꺼임
  React.useEffect(() => {
    text.current.addEventListener("mouseover", hoverEvent); //마우스 위로 올라왔을때 변경

    //unmount 될 때
    return () => {
      text.current.removeEventListener("mouseover", hoverEvent); 
    }
  }, [text]);
  
  return (
    <h1 ref={text}>텍스트입니다!</h1>
)
}

export default Text;

라우팅

Single Page Application(SPA)

말 그대로 서버에서 주는 html이 1개 뿐인 어플리케이션.
전통적인 웹사이트는 페이지를 이동할 때마다 서버에서 html, css, js(=정적자원들)을 내려준다면, SPA는 딱 한번만 정적자원을 받는다.

  • 왜 굳이 html을 하나만 줄까?
    많은 이유가 있지만, 그 중 제일 중요한 건 사용성 때문입니다.
    페이지를 이동할 때마다 서버에서 주는 html로 화면을 바꾸다보면 상태 유지가 어렵고, 바뀌지 않은 부분까지 새로 불러오니까 비효율적
    (사용자가 회원가입하다가 적었던 내용이 날아갈 수도 있고,
    블로그같은 경우, 페이지마다 새로 html을 받아오면 바뀐 건 글 뿐인데 헤더와 카테고리까지 전부 다시 불러와야 합니다.)

  • 단점은 없나?
    → 단점도 있어요. SPA는 딱 한 번 정적자원을 내려받다보니, 처음에 모든 컴포넌트를 받아옵니다.
    즉, 사용자가 안들어가 볼 페이지까지 전부 가지고 옵니다. 게다가 한 번에 전부 가지고 오니까 아주아주 많은 컴포넌트가 있다면 첫 로딩 속도가 느려집니다.

SPA는 주소를 어떻게 옮길 수 있을까?
html은 딱 하나를 가지고 있지만, SPA도 브라우저 주소창대로 다른 페이지를 보여줄 수 있다. 브라우저 주소에 따라 다른 페이지를 보여주는 걸 라우팅이라고 한다.

리액트 라우팅

react-router-dom 설치
yarn add react-router-dom

적용방법

1. index.js에 BrowserRouter 적용하기
import React from 'react';
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

// 이부분이 index.html에 있는 div#root에 우리가 만든 컴포넌트를 실제로 랜더링하도록 연결해주는 부분입니다.
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

BrowserRouter(브라우저라우터)는 웹 브라우저가 가지고 있는 주소 관련 정보를 props로 넘겨준다.


2. App.js에서 Route 적용하기

<Link/> 사용 방법
링크 컴포넌트는 html 중 a 태그와 비슷한 역할. 리액트 내에서 페이지 전환을 한다.
<Link to="주소">[텍스트]</Link>

import {Link, Route} from 'react-router-dom';
import Cat from './Cat';
import Dog from './Dog';
import Home from './Home';

function App() {
  return (
    <div className="App">
    //링크 적용방법
      <div>
        <Link to="/">Home 가기</Link>
        <Link to="/cat">Cat 가기</Link>
        <Link to="dog/">Dog 가기</Link>
      </div>
     //라우트 적용방법
      <Route path='/'>
        <Home />
      </Route>
      <Route path='/cat'>
        <Cat />
      </Route>
      <Route path='/dog'>
        <Dog />
      </Route>
    </div>
  );
}

export default App;

3. exact 적용하기

/cat, /dog에서 Home컴포넌트가 같이 나오는거 방지('/'가 포함되기 때문에)

<Route path='/' exact>
  <Home />
</Route>

<Route path='/cat'>
  <Cat />
</Route>

<Route path='/dog'>
  <Dog />
</Route>

4. URL 파라미터사용하기
  • 파라미터 주는 방법
<Route path='/cat/:cat_name'>
  <Cat />
</Route>

/cat/뒤에 주소가 동적으로 바뀐다(동적 라우팅)
:cat_name은 어떤 변수명으로 받아올 것인가
ex)/cat/perl, cat/nabi

  • 파라미터 사용방법 1 - useParams 훅을 사용
    주소에 /cat/perl로 입력되었으면
// Cat.js
import React from "react";
import { useParams } from "react-router-dom";

const Cat = (props) => {
    const cat_name = useParams();
    console.log(cat_name);
  return (
      <div>고양이 화면입니다!</div>
  );
};

export default Cat;

콘솔에 {cat_name: "perl"}로 나온다.

  • 파라미터 사용방법2 - component-props로 받아오기
    App.js에서 넘기기. component={Cat}을 사용
<Route path='/cat:cat_name' component={Cat}>
  <Cat />
</Route>

Cat.js에서 받아오기

import React from "react";

const Cat = (props) => {
	console.log(props);
	return <div>고양이 화면입니다!</div>;
};

export default Cat;

콘솔에 모든 객체가 다 나옴


useHistory 사용하기

import React from "react";
import { useHistory } from "react-router-dom";//useHistory가져옴

const Home = (props) => {
  let history = useHistory(); //useHistory를 history로
  return (
    <>
      <div>메인 화면이에요.</div>
    
	//버튼을 누르면 cat페이지 이동
      <button onClick={() => {history.push("/cat");}}> 
        cat으로 가기
      </button>
    </>
  );
};

export default Home;

잘못된 주소 처리

NotFound.js 파일을 만든다

import React from "react";

const NotFound = (props) => {
  return <h1>주소가 올바르지 않아요!</h1>;
};

export default NotFound;

App.js에서 불러온다. Switch도 불러온다.
import NotFound from "./NotFound";
import { Route, Switch } from "react-router-dom";

경로를 <Switch>로 감싸준다. switch는 맞는 경로가 없으면 경로 지정 안한 마지막 페이지가 뜬다.

<Switch>
  <Route path='/' exact>
    <BucketList list={list} />
  </Route>
  <Route path='/detail/:index' >
    <Detail />
  </Route>
  <Route>
    <NotFound />
  </Route>
</Switch>

리덕스를 통한 리액트 상태관리

리액트에서는 state를 내려주기만 하고 자식이 부모의 state값을 수정하면 안된다. 또한 부모에서 자식이 많을 경우 계속 state를 전달해 줘야 하는 불편함이 있다. 따라서 전역으로 데이터를 관리 할 수 있는 리덕스가 필요하다.

  • (1) 리덕스 Store를 Component에 연결한다.
  • (2) Component에서 상태 변화가 필요할 때 Action을 부른다.
  • (3) Reducer를 통해서 새로운 상태 값을 만들고,
  • (4) 새 상태값을 Store에 저장한다.
  • (5) Component는 새로운 상태값을 받아온다. (props를 통해 받아오니까, 다시 랜더링 되겠죠?)

스토어는 데이터 저장소이고 component는 스토어에 있는 데이터 값을 구독한다.
component에서 데이터 값이 수정되면 스토어의 값을 바꿔달라고 리듀서한테 액션을 보낸다.(액션을 디스패치 했다)
리듀서에서 데이터 값을 수정하고 스토어에 저장한다.
스토어는 바뀐 값을 구독하고 있는 component에 전달한다.
그러면 화면에 새로운 데이터가 나타난다.


리덕스 살펴보기

리덕스 패키지 설치하기
yarn add redux react-redux

  • State
    리덕스에서는 저장하고 있는 상태값(데이터)
    딕셔너리 형태{key: value}형태로 보관합니다.
  • Action
    상태에 변화가 필요할 때(=가지고 있는 데이터를 변경할 때) 발생하는 것입니다.
// 액션은 객체예요. 이런 식으로 쓰여요. type은 이름같은 거예요! 저희가 정하는 임의의 문자열을 넣습니다.
{type: 'CHANGE_STATE', data: {...}}
  • ActionCreator
    액션 생성 함수라고도 부릅니다. 액션을 만들기 위해 사용합니다.
//이름 그대로 함수예요!
const changeState = (new_data) => {
// 액션을 리턴합니다! (액션 생성 함수니까요. 제가 너무 당연한 이야기를 했나요? :))
	return {
		type: 'CHANGE_STATE',
		data: new_data
	}
}
  • Reducer
    리덕스에 저장된 상태(=데이터)를 변경하는 함수입니다.
// 기본 상태값을 임의로 정해줬어요.
const initialState = {
	name: 'mean0'
}

function reducer(state = initialState, action) {
	switch(action.type){

		// action의 타입마다 케이스문을 걸어주면, 
		// 액션에 따라서 새로운 값을 돌려줍니다!
		case CHANGE_STATE: 
			return {name: 'mean1'};

		default: 
			return false;
	}	
}
  • store
    전역 데이터 저장소

  • dispatch
    액션을 발생 시키는 역할
    dispatch(action);


리덕스 써보기

덕스(ducks) 구조
덕스 구조는 모양새로 묶는 대신 기능으로 묶어 작성합니다.

redux 폴더 생성 후 안에 저장소인 configStore.js 파일, modules폴더를 만든다.

모듈 만들기

//modules폴더 안 bucket.js

// Actions 액션 타입을 정해줍니다.
const LOAD = "bucket/LOAD";
const CREATE = "bucket/CREATE";

// initialState 초기 상태값을 만들어줍니다.
const initialState = {
  list: ["영화관 가기", "매일 책읽기", "수영 배우기"],
};

// Action Creators 액션 생성 함수를 작성합니다.
// 액션 생성 함수예요.
// 액션을 만들어줄 함수죠!
export const loadBucket = (bucket) => {
  return { type: LOAD, bucket };
};

export const createBucket = (bucket) => {
  return { type: CREATE, bucket };
};

// Reducer 리듀서예요.
// 실질적으로 store에 들어가 있는 데이터를 변경하는 곳이죠!
export default function reducer(state = initialState, action = {}) {
  switch (action.type) {
    // do reducer stuff
    case "bucket/LOAD":
      return state;

    case "bucket/CREATE":
      const new_bucket_list = [...state.list, action.bucket];
      return { list: new_bucket_list };

    default:
      return state;
  }
}

스토어 만들기

//configStore.js
import { createStore, combineReducers } from "redux";
import bucket from "./modules/bucket";

// root 리듀서를 만들어줍니다.
// 나중에 리듀서를 여러개 만들게 되면 여기에 하나씩 추가해주는 거예요!
const rootReducer = combineReducers({ bucket });

// 스토어를 만듭니다.
const store = createStore(rootReducer);

export default store;

리덕스와 컴포넌트를 연결

index.js에서 스토어를 component에 주입

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from "react-router-dom";

// 우리의 버킷리스트에 리덕스를 주입해줄 프로바이더를 불러옵니다!
import { Provider } from "react-redux";
// 연결할 스토어도 가지고 와요.
import store from "./redux/configStore";

ReactDOM.render(
  <Provider store={store}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>,
  document.getElementById("root")
);

컴포넌트에서 리덕스 데이터 사용하기

  • 리덕스 훅
// useDispatch는 데이터를 업데이트할 때,
// useSelector는 데이터를 가져올 때 씁니다.
import {useDispatch, useSelector} from "react-redux";
  • redux 데이터 가져오기
    useSelector((state) ⇒ state.bucket)
    state는 리덕스 스토어가 가진 전체 데이터
import { useSelector } from "react-redux";
...
 const my_lists = useSelector((state) => state.bucket.list);
  • redux 데이터 추가하기
// useDispatch를 가져와요!
import {useDispatch} from "react-redux";
// 액션생성함수도 가져오고요!
import { createBucket } from "./redux/modules/bucket";
...
const dispatch = useDispatch();
const addBucketList = () => {
    dispatch(createBucket(text.current.value));
  };
profile
Web Frontend

0개의 댓글