반응형

redux 사용전 컴포넌트의 상태 변화의 전달 과정

redux를 사용 후 컴포넌트 외부에서 상태관리 과정

Redux를 사용하는 이유

 

리액트 프로젝트의 경우 대부분의 작업시 부모 컴포넌트를 통해 하위 컴포넌트의 데이터를 업데이트 한다.

(데이터의 연관이 있는 컴포넌트끼리 ref를 사용하여 전달할 수 있으나 코드가 꼬이는 문제로 인해 지양한다)

 

컴포넌트의 갯수가 적을때는 문제가 되지 않지만, 점점 늘어날수록 유지보수의 어려움이 발생한다.

예를 들어 변수명의 변경을 하면 연관된 컴포넌트 파일 모두에 수정을 거쳐야된다.

그러나 리덕스를 사용하게 되면 데이터 상태를 컴포넌트 외부에서 관리하기 때문에 과정을 단순화 시킬 수 있게 된다.

 

- Reducer : 업데이트 로직을 정의하는 함수

 

< Redux 흐름 >

subscribe(상태 변화 감지요청) >> action(상태 변화) >> dispatch(상태업데이트, store에 action 전달)

>> Store(state 갱신) >> listener(상태 변화 알림) >> 컴포넌트 리렌더링

 

 

- 여러 컴포넌트를 거칠 필요없이 부모 컴포넌트에서 다이렉트로 받는거처럼 리덕스 스토어에서 원하는 상태값을 전달한다.

(단방향 데이터 흐름으로 데이터 구조의 단순화)

 

< Redux 의 3 원칙 >

1. Single source of truth
    애플리케이션 내에 Store는 반드시 1개 뿐. Store는 반드시 1개만 존재한다.

2. State is read-only
    state를 직접 변경해서는 안.된.다.
    state를 변화시키는 유일한 방법은 Action을 Reducer에 dispatch(송신, 전달)하는 방법 뿐이다.
    즉, state의 변경은Reducer만 할 수 있다. Reducer 이외의 공간에서는 state는 읽기 전용인 것이다.

 

3. Changes are made with pure functions
    Reducer는 순수 함수여야만 한다.
    Reducer 함수는 parameter로 기존의 state와 Action을 받는데, 

    Reducer 함수는 기존의 state를 직접 변경하지 않고, 새.로.운 state object(객체)를 작성해서 return해야한다.


useReducer

useReducer와 context api 로 redux의 기능을 대부분 구현할 수 있다

(그러나 프로젝트의 규모가 크다면 useReducer와 context api 조합의 한계로 비동기적인 작업시 불편하다)

 

import React, { useState, useReducer } from 'react';

const initialState = { //state 정의
	winner: '',
    turn : 'O',
    tableData : [['','',''], ['','',''], ['','','']],
};

const reducer = (state, action) => { //state 변화 정의
	
};
const [state, dispatch] = useReducer(reducer, initialState);

 

- 여러개의 state를 하나의 state로 만들어 state의 갯수를 줄여준다

 

< Redux와 useReducer 차이 >

 

1. 리덕스는 dispatch를 통해 state가 동기적으로 변경, useReducer는 비동기적으로 변경

 

table 최적화 하기

 

useEffect를 사용하여 렌더링 될 때 console.log로 두번째인자(바뀌는 값)을 넣어서 어떠한 값의 변화로 인한 리렌더링인지 확인할 수 있다.

최종 하위 컴포넌트(td) 값의 변화로 리렌더링 될때 부모 컴포넌트까지 렌더링되지 않도록 memo를 사용하여 props의 값을 기억해준다.

 

 

** React.memo : 기억해둔 props의 값이 같다면 기억해둔 값을 재사용, 다르다면 리렌더링

 

** useCallback :  함수 자체를 메모이징해서 재사용한다. 불필요한 렌더링을 줄일수 있다.


//부모 컴포넌트
import React, { useReducer, createContext } from 'react';

const Context = createContext({initial});
const value = useMemo(() => ({tableData : state.tableData, dispatch}), [state.tableData]);

<Context.Provider value={ value }>
	<Form />
    <Table />
</Context.Provider>


//하위 컴포넌트
import React, { useContext } from 'react';
import { Context } form '부모컴포넌트';

const Table = () => {
	const { tableData } = useContext(Context);
    ...
};

export default Table;

Context Api

context api를 제외하고 useReducer만 사용할 경우, dispatch 와 reducer를 통해 변경할 데이터(action)을 props로 최종 변경할 컴포넌트까지 전달해주어야한다.

- 자식 컴포넌트 뿐 아니라 해당 데이터를 사용할 컴포넌트 모두에게 다이렉트로 전달할 수 있다.

 

1. createContext(기본값) 으로 Context 객체를 생성

2. Context.Provider : 하위 컴포넌트에 접근 가능하도록 명시

    - Context.Provider 의 value 속성으로 데이터 전달

    - value 속성에 객체로 명시해주면 렌더링시 계속 새로운 객체를 만들기 때문에 최적화에 문제가 발생, 

        >> useMemo를 사용(캐싱)하여 메모이징하여 해결한다. dispatch는 항상 같으므로 변경될 값에 기재하지 않아도된다.

 

3. useContext : Context(부모 컴포넌트에서 전달될 props)를 자식컴포넌트에서 불러오도록 해준다.

 

< Context API 최적화 >

 

memo, useMemo를 적절히 사용하여 캐싱을 통해 최적화

 

 

 

출처: https://recoderr.tistory.com/50

반응형
반응형

버튼 태그에 setCount 메소드를 onClick 이벤트에 등록해주는데,

setCount의 인자로 함수를 보냅니다.

+ 버튼을 클릭하면 이전상태로부터 1을 증가시키는 함수가 setCount의 인자로 들어가게 됩니다.

 

간단한 경우에는 useState를 쓰는 것이 더 좋다고 생각하지만

현재의 상태에 액션을 받아서 새로운 상태로 갱신하는(reducer) 형태인 경우 useReducer를 사용해야 합니다.

  1. 다수의 하윗값을 포함하는 정적 로직을 만드는 경우
  2. 다음 state가 이전 state에 의존적인 경우

이러한 경우에 useState보다 useReducer가 선호됩니다.

 

useReducer는 첫 번째 인자로 reducer 함수를 받고, 두 번째 인자로는 initialState 객체를 받습니다.

import React, { useReducer } from 'react';

const initialState = { count: 0 };

const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
};

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      Count : {state.count}
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
    </>
  );
};

export default Counter;

 

useReducer는 state와 dispatch를 반환하기 때문에 dispatch로 액션을 보내는 함수를 onClick 이벤트에 등록해 준 것을 알 수 있습니다. 그러면 RESET 버튼은 어떻게 구현할까요?

import React, { useReducer } from 'react';

const init = (initialCount) => {
  return { count: initialCount };
};

const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return init(action.payload);
    default:
      throw new Error();
  }
};

const Counter = ({ initialCount }) => {
  const [state, dispatch] = useReducer(reducer, initialCount, init);

  return (
    <>
      Count : {state.count}
      <button
        onClick={() => dispatch({ type: 'reset', payload: initialCount })}
      >
        RESET
      </button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
    </>
  );
};

export default Counter;

init 함수를 만들어 useReducer의 세 번째 인자로 전달해 주면 됩니다.

 

잘 동작합니다!

출처: https://clearwater92.tistory.com/26

반응형
반응형

css 가상요소(Pseudo-Element) :before :after사용법

 
 
*** 들어가기 전에***
가상선택자, 가상클래스, 가상요소??
 
가상클래스

CSS pseudo-class 는 선택될 요소(element)의 특별한 상태를 지정하는 선택자(selector)에 추가된 키워드입니다. 예를 들어 :hover는 사용자가 선택자에 의해 지정된 요소 위를 맴돌(hover) 때 스타일을 적용합니다.

가상 클래스(pseudo-class)는, 가상 요소(pseudo-elements)와 함께, 문서 트리의 콘텐츠 뿐만 아니라, 탐색기 히스토리 (가령, :visited), 콘텐츠 상태(일부 폼 요소의 :checked 같은) 혹은 마우스 위치 (마우스가 요소 위인지 알 수 있는 :hover 같이)처럼 외부 요인(factor) 관련 요소에 스타일을 적용할 수 있습니다.

 

 
 
가상요소
가상 클래스(pseudo-classes)처럼, 가상 요소(pseudo-element)는 선택자(selector)에 추가되지만 특별한 상태를 기술하는 대신, 문서의 특정 부분을 스타일할 수 있습니다. 예를 들어, ::first-line 가상 요소는 선택자에 의해 지정된 요소의 첫 줄만을 대상으로 합니다.
 
 
 
 
 
:before
:after

가상선택자는 꾸밈을 위해서 의미없는 태그를 더 추가해야 될 때, 태그 대신에 가상으로 처리해 주는 쓸모 많은 css 기능입니다. 만일 html 구조에서 필요한 내용이라면 가상선택자로 만들면 안되겠죠~

  1. 어떤 사항일 때 css 가상선택자를 사용하나요?
의미없는 태그를 쓰지 않도록 도와줌, 태그 최소화.
  • 목록형 꾸밈
  • 버튼 꾸밈
  • 간단한 아이콘 만들기(닫기버튼, 화살표 등)  ** 도형을 만들 때 주의할 점: 보여지는 픽셀두께가 세밀하지 않기 때문에 비주얼적으로 중요한 아이콘이면 이미지를 쓰는 것을 추천
  • 이미지 아이콘을 넣고 싶을 때
  • 꾸밈 한글을 넣고 싶을 때 (tag의 #, 또는 콤마 등)
.class-name:before {
	content:'';
	width:;
	height:;
	
}

:before 해당 태그의 앞에 놓여진다.
:after 해당 태그의 다음 위치에 놓여진다.

content:'' : 가상선택자에 필수로 들어가는 요소. 작음따음표'' 안에는 텍스트 내용을 넣고, 없으면 작은따음표만 넣기
가상선택자는 부피가 없으므로, 아이콘을 표현할 땐 꼭 너비와 높이를 정해주어야 한다. transform을 쓸 때는 블럭요소(display:block 또는 display:inline-block)가 되어야 적용가능하다.

:before 와 ::before의 차이

차이는 없다. 오히여 더블콜론(::)::before을 쓴다면 ie8 이하 버전은 적용이 되지 않기 때문에 클론 하나만(:):before 쓰는 것이 좋다고 볼 수 있다.
css2에서는 콜론이 하나였다가, css3에는 더블클론으로 바뀌었다.

가상선택자 적용이 안되는 경우

  1. 폼(form, input...) 태그와 이미지 태그(img)는 가상선택자가 적용되지 않는다.(이 부분은 지금까지도 몰랐던 사실.. 예시를 만들면서 알게됨)
    가상선택자 이슈
  2. ie7 이하버전은 지원하지 않는다.
  3. ie8 이하버전은 더블콜론(::)이 적용되지 않음

 

 

 

출처: https://green-webdesigner.tistory.com/20

반응형
반응형

Vue.js에서 computed 프로퍼티는 매우 유용하게 사용된다. 그러나 처음 Vue.js를 시작할때 computed와 watch가 모두 반응형이라는 키워드과 관련이 있기 때문에 이 둘을 혼동하곤 했다. Vue.js의 강점을 잘 살려서 코딩을 하기위해 이 두가지 키워드를 잘 알고 있어야 한다.

Computed — 반응형 getter

computed를 한마디로 얘기하자면 “반응형 getter”이다. 아래 예시를 보자. vue.js 공식가이드 문서에 나오는 예시와 동일한 로직을 옮긴것이다.

<template>
  <div>
    <p>원본 메시지: "{{ message }}"</p>
    <p>역순으로 표시한 메시지: "{{ reversedMessage }}"</p>
  </div>
</template>

<script>
export default {
  name: 'test',
  data(){
    return {
      message: '안녕하세요'
    }
  },
  computed: {
    reversedMessage: function () {
      return this.message.split('').reverse().join('')
    }
  }
}
</script>

computed 프로퍼티를 보면 reverseMessage 라는 프로퍼티에 값으로 익명함수가 할당되어있다. computed에 정의하는 이 익명함수는 반드시 값을 리턴하도록 작성되야한다.

getter

computed의 reverseMessage 프로퍼티가 정될때 내부적으로는 Object.defineProperty를 통해 정의되며, 이때 익명함수가 getter로 설정된다. reverseMessage 를 함수가 아니라 일반 객체처럼 사용할 수 있는점과 호출될때만 계산이 이루어지고, 계산결과가 캐싱되는 특성이 생기게 된것은 getter의 특성덕분이다(이는 methods와의 차이를 유발하는 지점이기도 하다). 하지만 바로 이점 때문에 값이 변하게 되어도 캐싱때문에 변경된 값을 인지하지 못하는 단점이 생기게된다.

반응형(reactive)

Vue.js 는 이 단점을 상쇄하고 반응형을 구현하기 위해 특별한 장치를 한다. getter 함수 내에 속한 프로퍼티의 변경여부를 추적하는 것이다.(마이구미님 글 참고) 위 예시에서는 message 를 감시하고 있다가 message의 값이 변경되면 reverseMessage 를 다시 계산한다. 결국, computed는 사용하기 편하고, 자동으로 값을 변경하고 캐싱해주는 아주 끝내주는 “반응형 getter”라 부를 수 있겠다. (반응형은 Computed뿐 아니라 Vue.js 의 전반의 주요한 특징으로 볼 수 있다.)

Watch — 반응형 콜백

변경을 감시(watch)한다는 점 때문에 computed와 watch를 혼동할 수 있다.걱정할 필요는 없다. computed에 비해 watch는 단순하고 이해하기 쉽기 때문이다. watch는 Vue 인스턴스의 특정 프로퍼티가 변경될때 지정한 콜백함수가 실행되는 기능이다. 위 예시를 응용한다면 아래와 같을 것이다.

<template>
  <div>
    <p>원본 메시지: "{{ message }}"</p>
    <p>역순으로 표시한 메시지: "{{ reversedMessage }}"</p>
  </div>
</template>

<script>
export default {
  name: 'test',
  data(){
    return {
      message: '안녕하세요',
      reversedMessage: ''
    }
  },
  watch: {
    message: function (newVal, oldVal) {
      this.reversedMessage = newVal.split('').reverse().join('')
    }
  }
}
</script>

watch를 정의한 부분(17~21)을 보면 message 프로퍼티에 익명함수가 할당되어있다. 이 익명함수가 콜백함수 역할을 할 것이고, message 프로퍼티가 변경되면 변경된 값을 콜백함수의 첫번째 인자로 전달하고, 이전 값을 두번째 인자로 전달하여 실행한다. computed가 새 프로퍼티를 생성하고 그것의 getter 로 익명함수를 설정되는 것과는 달리 watch는 아무 프로퍼티도 생성하지 않고 익명함수는 단순히 콜백함수로의 역할을 한다. watch에 명시된 프로퍼티는 감시할 대상을 의미할 뿐이다.

어떻게 사용할 것인가

  • 위의 예시처럼 인스턴스의 data에 할당된 값들 사이의 종속관계를 자동으로 세팅하고자 할때는 computed로 구현하는것이 좋다. 그러니까 reverseMessage  message 값에 따라 결정되어진다. 이 종속관계가 조금이라도 복잡해지면 watch로 구현할 경우 중복계산이 일어나거나 코드 복잡도가 높아질 것이다. 이는 오류도 더 많이 발생시킬 것이다.
  • watch는 특정 프로퍼티의 변경시점에 특정 액션(call api, push route …)을 취하고자 할때 적합하다.
  • computed의 경우 종속관계가 복잡할 수록 재계산 시점을 예상하기 힘들기 때문에 종속관계의 값으로 계산된 결과를 리턴하는 것 외의 사이드 이펙트가 일어나는 코드를 지양해야한다.
  • 더 쉽게 판단하는 방법: 만약 computed로 구현가능한 것이라면 watch가 아니라 computed로 구현하는것이 대게의 경우 옳다.

참고 문서

출처: https://jeongwooahn.medium.com/vue-js-watch%EC%99%80-computed-%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%99%80-%EC%82%AC%EC%9A%A9%EB%B2%95-e2edce37ec34

반응형

+ Recent posts