n = int(input())
arr = [list(map(int, input().split())) for _ in range(n)]
counts = [1 for _ in range(n)]
for i in range(n):
for j in range(n):
if((arr[i][0] < arr[j][0]) and (arr[i][1] < arr[j][1])):
counts[i] += 1
print(*counts)
위의 코드에서 dispatch 함수의 인자로 '액션 생성함수(action creator)'를 넣어줬다. 즉, modules 폴더에서 작성한 리듀서 파일 안의 액션 생성 함수들의 결과로 '액션 객체' 값을 리턴 받아 dispatch 하게 된다. 그런 다음 reducer에서 액션 타입에 맞는 로직을 수행하게 되고 store의 상태 값을 변화시키게 된다.
// /components/TodoAppContainer.tsx
import React from "react";
import TodoApp from "./TodoApp";
import { useSelector } from "react-redux";
import { RootState } from "../modules";
const TodoAppContainer = () => {
// useSelector :: 간단하게 리듀서 모듈과 연결해 데이터를 받아올 수 있다.
const todos = useSelector((state: RootState) => state.todoReducer);
return <TodoApp todos={todos} />;
};
export default TodoAppContainer;
useSelector는connect를 통해 상태 값을 조회하는 것보다 훨씬 간결하게 작성하고 코드 가독성이 상승되는 장점이 있는 함수다.
Redux는 애플리케이션 상태를 관리하기 위한 오픈 소스 JavaScript 라이브러리다.
1. Redux 등장 배경
리덕스가 등장하기 이전 프론트엔드에서 데이터 흐름(형상)을 관리하는 방식은 MVC 패턴이었다. MVC 패턴의큰 특징 중 하나가 ‘양방향 데이터 흐름’이다. 모델이 변경된다면 뷰 또한변경되고, 사용자에 의해 뷰에서 변경이 일어난다면 모델 또한 변경된다. 이러한 양방향 데이터 흐름은 설계하기 간단하고 코드 작성하기 쉬운 장점이 있다. 하지만 애플케이션 규모가 커진다면 문제가 생긴다. 한 개의 모델이 여러 개의 뷰를 조작하고 한 개의 뷰가 여러 개의 모델을 조작한다면 데이터 흐름을 이해하기 힘들어진다. 버그를 찾기 어려워지고 데이터 흐름을 추적하는 데 많은 시간을 투자해야 하는 단점이 있다.
Model— 데이터의 형식이나 구조를 관리한다. 모델에 맞지 않는 데이터는 흐름을 제어받을 수 있다.
View— 코드가 사용자에게 보여지는 부분을 담당한다. 사용자에게 보여지는 모습과 형태를 관리한다.
Controller— 변화하는 데이터를 관리한다. View에서 발생하는 이벤트로 변경되는 데이터나 서버로부터 받은 변경된 데이터를 Model과 View에 업데이트해준다.
출처: http://aalmiray.github.io/griffon-patterns
2. Flux 등장
페이스북에서 MVC 패턴으로 데이터 흐름을 관리하는 데 많은 어려움을 겪고 있었고 그의 대안으로 Flux라는 새로운 아키텍처 패턴을 개발하였다.
Flux는 MVC 패턴에서 겪은 복잡한 상황을 개선하는 것이 목적이었고 그 방법으로’ 단방향 데이터 흐름’을 적용한 것이다.
View는 MVC 패턴과 달리 데이터를 변경시키지 않고 Action을 넘겨준다.
Action은 반드시 Dispatcher를 지나게 되고 Dispatcher를 통해서 데이터 변경이 일어나고 View는 변경된 데이터를 Store를 통해서 전달받는다.
이러한 단방향 데이터 흐름은 기존의 MVC 패턴에 있던‘상태의 전이’(뷰와 모델 사이의 데이터 변경이 연결된 수많은 곳으로 따라 변경되는 현상) 현상을 없애주고‘예측 가능하다’는 특징을 갖는다.
3. Redux의 등장
2015년에 Dan Abramov에 의해서 React + Flux의 구조에 ‘Reducer’를 결합한 ‘Redux’가 등장하게 된다.
리덕스의 특징 3가지
1. Single source of truth
모든 상태는 하나의 스토어 안에 하나의 객체 트리 구조로 저장
2. State is read-only
상태를 변화시키는 유일한 방법은 무슨 일이 벌어지는 지를 묘사하는 액션 객체를 전달하는 방법뿐이다. 데이터의 변경은 Reducer만 할 수 있다. Reducer 이외의 공간에서는 데이터(상태)는 읽기 모드인 것이다.
3. Changes are made with pure functions
변화를 일으키는 함수, 리듀서는 순수한 함수여야 한다.
리듀서 함수는 이전 상태와, 액션 객체를 파라미터로 받습니다.
이전의 상태는 절대로 건드리지 않고, 변화를 일으킨 새로운 상태 객체를 만들어서 반환합니다.
똑같은 파라미터로 호출된 리듀서 함수는언제나똑같은 결괏값을 반환해야만 합니다.
만약 매번 같은 입력에 대해 같은 결과를 리턴하지 않는 경우는, 예를 들어 랜덤 숫자를 생성한다던지 혹은, 네트워크에 요청을 한다던지 등 이러한 작업은 결코 순수하지 않은 작업이므로, 리듀서 함수의 바깥에서 처리해줘야 한다. 그런 것을 하기 위해서,리덕스 미들웨어를 사용할 수 있다.
리덕스의 구성요소
- Store
상태를 관리하고 저장되는 장소이며, 직접적으로 스토어에 접근해서는 안되고 dispatch, subscribe, getState 같은 함수들을 이용해 스토어의 state에 접근한다. 스토어는 현재의 앱 상태와, 리듀서가 들어가 있고, 추가적으로 몇 가지 내장 함수들을 포함하고 있다.
Dispatch 함수 : 액션을 발생시켜 store에 상태 변화가 필요하다는 것을 알리는 역할을 한다. 호출된 액션은 reducer 함수를 호출시키고, 액션에 맞는 로직과 상태 변화 과정을 거친다.
getState 함수 : 현재 애플리케이션의 state 값에 접근한다.
Subscribe 함수 : 함수 형태의 값을 파라미터로 받고, action이 dispatch 될 때마다 전달받은 함수를 호출한다.
- Reducer 함수
dispatch에 의해 액션이 전달되면, 액션 객체 값과 기존 state 값을 참조해 새로운 state 값을 반환해주는 역할을 한다.
메모이제이션이란 계산된 값을 자료구조에 저장하고 이후 같은계산을 반복하지 않고 자료구조에서 꺼내 재사용하는 것을 말한다. 메모이제이션의 대표적인 예로는동적계획법의 탑다운 방식이 있다.
1. 기본 개념
useMemo와useCallback는 메모이제이션 기능을 지원하는 리액트의 내장 훅으로,퍼포먼스 최적화를 위하여 사용된다.
useMemo는 메모이제이션된 값을 반환한다.
useCallback은 메모이제이션된 콜백을 반환한다.
리액트는 실제로는 상태가 변경되는 컴포넌트와 그 이하의 모든 자식 컴포넌트가 랜더링의 대상이 된다. 문제는 자식 컴포넌트의 상태가 변경되지 않아도(갱신될 필요가 없어도) 불필요한 랜더링이 일어난다는 것이다. 자바스크립트에서 함수도 참조형 데이터이기 때문에 늘 새로운 값으로 취급되어 동일성을 보장받지 못하므로 리액트에서 매번 새로운 렌더링 대상이 된다.
콜백과 그것의 의존성 값을 배열로 만들어 인자로 전달하며, 메모이제이션된 콜백함수를 반환한다. 그 메모이제이션된 버전은 콜백의 의존성이 변경되었을 때에만 변경된다. 이것은, 불필요한 렌더링을 방지하기 위해(예로shouldComponentUpdate를 사용하여) 참조의 동일성에 의존적인 최적화된 자식 컴포넌트에 콜백을 전달할 때 유용하다.
useCallback(fn, deps)은useMemo(() => fn, deps)와 같다고 한다.
useEffect와 마찬가지로 두 번째 인자로 빈 배열([])을 넣으면 어떤 상태 값에도 반응하지 않으며, 두 번째 인자로 아무것도 넣지 않으면 모든 상태 변화에 반응한다.