redux .1
제가 만들고 있는 포트폴리오 페이지에 다크모드를 만드는 것으로 리덕스를 사용해 보겠습니다. 물론 작은 프로젝트라서 필요하다고 할수는 없지만 그래도 리덕스의 장점을 보여줄수 있는 예라고 생각합니다. 글로벌로 스테이트를 관리하고 어떤 컴포넌트에서든지 그 스테이트의 상태를 받아 상황에 맞게 랜더링을 해줄 수 있습니다.
const store = createStore(reducer);
ReactDOM.render(
,
document.getElementById("root")
);
createStore(reducer) 로 스토어를 만들고
프로바이더에 props 로 넘겨준다.
리덕스에서 액션을 정의 한다.
액션에 이름을 주는 것이라 생각하면 편한것 같습니다.
저는 아래와 같이 액션의 이름을 지정하였습니다.
export const DARK_MODE = "darkMode";
export const LIGHT_MODE = "lightMode";
액션을 생성합니다 아까 위에 정의를 해둔 액션의 액션생성자 함수를 만듭니다.
import * as types from "./actions";
export function darkMode() {
return {
type: types.DARK_MODE
}
}
export function lightMode() {
return {
type: types.LIGHT_MODE
}
}
우선 아까 정의 해둔 액션을 import해줍니다.
그리고 함수를 작성하고 export해줍니다.
리턴하는것은 어떤 액션을 가지고 올지에 대한것인데
객체로 리턴되고 항상 키로 type을 가지고 있어야 합니다.
다크모드함수가 실행될때 어떤 액션을 할것이냐에 대한것입니다.
import * as mode from "../actions/actions";
const initialState = { mode: false};
export default function modeChanger(state = initialState, action){
switch (action.type) {
case mode.DARK_MODE: return {
mode: true
};
case mode.LIGHT_MODE: return {
mode: false
};
default: return state;
}
}
reducer가 하는일은 액션이 행해질때 어떤 일을 할것인가에 대한 것입니다.
저는 modeChanger라는 함수로 이전 스테이트와 액션을 인수로 받고 함수를 실행합니다.
그 액션이 다크모드라면 모드라는 스테이트를 true로 변경하고 라이트 모드라면 false로 바꾸는 함수입니다.
둘다 아니라면 그냥 이전 스테이트를 반환합니다.
const mapStateToProps = state => {
return {
mode: state.reducers.mode
};
};
const mapDispatchToProps = dispatch => {
return { handleDarkMode: () => { dispatch(actions.darkMode());
},
handleLightMode: () => {
dispatch(actions.lightMode());
}
};
export default connect(mapStateToProps, mapDispatchToProps)(component);
다음은 리액트 리덕스가 지원하는 기능입니다.
mapStateToprops는 현재 컴포넌트에 props로 mode:state.reducers.mode를 넘겨줍니다.
mapDispatchToProps는 handleDarkMode가 실행될때 dispatch로 darkMode함수를 실행시킵니다.
아래와 같이 커넥트를 임포트 합니다.
import { connect } from “react-redux”;
export default connect(mapStateToProps, mapDispatchToProps)(componentName);
컴포넌트에 mapStateToProps, mapDispatchToProps를 연결시킵니다.
console.log("store로부터 받아오는 것", this.props)
위의 콘솔로그로 해당컴포넌트에 store로부터 받아오는 것이 무언인지를 확인 할수있습니다.
onClick={ this.props.mode ? this.props.handleLightMode : this.props.handleDarkMode }
위와 같이 이벤트 핸들러에 사용도 가능합니다.
this.prop.mode는 제가 미리 스토어에 생성해둔 state입니다. 그것의 상태를 해당 컴포넌트로 받아옴으로써 상태를 체크하여 상황에 따라 미리 생성해둔 액션들을 실행시켜 다시 스토어의 state 값을 변화 시킵니다.
const result : any = useSelector(selector : Function, equalityFn? : Function)
부
선택기 기능을 사용하여 Redux 스토어 상태에서 데이터를 추출 할 수 있습니다.
참고 : 선택기 기능은 여러 번 임의의 시점에서 실행될 수 있으므로 순수 해야합니다 .
선택기는 개념적으로 mapStateToProps주장하는connect 것과 거의 같습니다 . 선택기는 전체 Redux 저장소 상태를 유일한 인수로 사용하여 호출됩니다. 함수 구성 요소가 렌더링 될 때마다 선택기가 실행됩니다. useSelector()또한 Redux 스토어에 가입하고 작업이 발송 될 때마다 선택기를 실행합니다.
그러나 전달 된 선택기 useSelector()와 mapState함수 에는 약간의 차이 가 있습니다.
선택기는 개체뿐만 아니라 모든 값을 반환 할 수 있습니다. 선택기의 반환 값이 useSelector()후크 의 반환 값으로 사용됩니다 .
작업이 전달되면 useSelector()이전 선택기 결과 값과 현재 결과 값의 참조 비교를 수행합니다. 서로 다르면 구성 요소를 다시 렌더링해야합니다. 동일하면 구성 요소가 다시 렌더링되지 않습니다.
선택기 함수는 인수를 받지 않습니다ownProps . 그러나 소품은 클로저 (아래 예 참조) 또는 카레 선택기를 사용하여 사용할 수 있습니다.
메모리 셀렉터를 사용할 때는 각별히주의해야합니다 (자세한 내용은 아래 예 참조).
useSelector()===얕은 평등이 아닌 기본적으로 엄격한 기준 평등 검사를 사용합니다 (자세한 내용은 다음 섹션 참조).
참고 : 선택기에서 소품을 사용하는 경우 오류가 발생할 수있는 가장 중요한 경우가 있습니다. 자세한 내용 은이 페이지 의 사용 경고 섹션을 참조하십시오.
useSelector()단일 함수 구성 요소 내에서 여러 번 호출 할 수 있습니다 . 각 호출 useSelector()은 Redux 스토어에 대한 개별 구독 을 생성합니다. (가)에 사용되는 업데이트 배치 행동 반응의 돌아 오는 v7의 반응 때문에, a는 여러 원인이 작용 파견 useSelector()새 값을 반환하기 위해 동일한 구성 요소들 한다 단일 재 렌더링 결과를.
평등 비교 및 업데이트
함수 구성 요소가 렌더링되면 제공된 선택기 함수가 호출되고 결과가 useSelector()후크 에서 리턴됩니다 . 선택기가 실행되었고 변경되지 않은 경우 캐시 된 결과가 리턴 될 수 있습니다.
그러나 조치가 Redux 상점으로 디스패치 될 때 useSelector()선택기 결과가 마지막 결과와 다른 경우에만 다시 렌더링합니다. v7.1.0-alpha.5부터 기본 비교는 엄격한 ===참조 비교입니다. 이는 통화 connect()결과에 대해 얕은 동등성 검사를 사용하여 mapState다시 렌더링이 필요한지 여부를 결정 하는 것과 다릅니다 . 이것은 당신이 어떻게 사용해야하는지에 대한 몇 가지 의미를 가지고 있습니다 useSelector().
를 사용 mapState하면 모든 개별 필드가 결합 된 개체로 반환되었습니다. 반환 객체가 새로운 참조인지 여부는 중요하지 않았습니다 connect(). 개별 필드를 비교했을뿐입니다. 을 사용하면 useSelector()매번 새 객체를 반환하면 항상 기본적으로 다시 렌더링됩니다. 상점에서 여러 값을 검색하려는 경우 다음을 수행 할 수 있습니다.
전화 useSelector()하나의 필드 값을 반환 각 호출에 여러 번
Reselect 또는 유사한 라이브러리를 사용하여 하나의 객체에서 여러 값을 반환하지만 값 중 하나가 변경된 경우에만 새 객체를 반환하는 메모 화 된 선택기를 만듭니다.
다음과 shallowEqual같이 React-Redux 의 함수를에 대한 equalityFn인수로 사용하십시오 useSelector().
import { shallowEqual, useSelector } from 'react-redux'
// later
const selectedData = useSelector(selectorReturningObject, shallowEqual)
부
선택적 비교 기능은 Lodash _.isEqual()또는 Immutable.js의 비교 기능 과 같은 기능을 사용할 수도 있습니다 .
useSelector 예
기본 사용법 :
import React from 'react'
import { useSelector } from 'react-redux'
export const CounterComponent = () => {
const counter = useSelector(state => state.counter)
return
부
추출을 통해 소품을 사용하여 추출 할 대상 결정 :
import React from 'react'
import { useSelector } from 'react-redux'
export const TodoListItem = props => {
const todo = useSelector(state => state.todos[props.id])
return
부
메모 선택기 사용
useSelector위에 표시된대로 인라인 선택기와 함께 사용 하면 구성 요소가 렌더링 될 때마다 선택기의 새 인스턴스가 생성됩니다. 선택기가 상태를 유지하지 않는 한 작동합니다. 그러나 메모 선택기 (예 : createSelectorfrom을 통해 생성 reselect)는 내부 상태를 가지므로 사용시주의를 기울여야합니다. 아래에는 선택기 메모에 대한 일반적인 사용 시나리오가 나와 있습니다.
선택기가 상태에만 의존하는 경우 각 렌더링에 동일한 선택기 인스턴스가 사용되도록 구성 요소 외부에서 선언해야합니다.
import React from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
const selectNumOfDoneTodos = createSelector(
state => state.todos,
todos => todos.filter(todo => todo.isDone).length
)
export const DoneTodosCounter = () => {
const NumOfDoneTodos = useSelector(selectNumOfDoneTodos)
return
export const App = () => {
return (
<>
Number of done todos:
</>
)
}
부
선택기가 구성 요소의 소품에 의존하지만 단일 구성 요소의 단일 인스턴스에서만 사용되는 경우에도 마찬가지입니다.
import React from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
const selectNumOfTodosWithIsDoneValue = createSelector(
state => state.todos,
(_, isDone) => isDone,
(todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
)
export const TodoCounterForIsDoneValue = ({ isDone }) => {
const NumOfTodosWithIsDoneValue = useSelector(state =>
selectNumOfTodosWithIsDoneValue(state, isDone)
)
return
export const App = () => {
return (
<>
Number of done todos:
</>
)
}
부
그러나 선택기가 여러 구성 요소 인스턴스에 사용되고 구성 요소의 prop에 종속 된 경우 각 구성 요소 인스턴스가 자체 선택기 인스턴스를 가져와야합니다 ( 이 이유가 필요한 이유에 대한 자세한 설명 은 여기 참조 ).
import React, { useMemo } from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
const makeNumOfTodosWithIsDoneSelector = () =>
createSelector(
state => state.todos,
(_, isDone) => isDone,
(todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
)
export const TodoCounterForIsDoneValue = ({ isDone }) => {
const selectNumOfTodosWithIsDone = useMemo(
makeNumOfTodosWithIsDoneSelector,
[]
)
const numOfTodosWithIsDoneValue = useSelector(state =>
selectNumOfTodosWithIsDone(state, isDone)
)
return
export const App = () => {
return (
<>
Number of done todos:
Number of unfinished todos:
</>
)
}
부
제거 : useActions()
useDispatch()
const dispatch = useDispatch()
부
이 후크는 dispatchRedux 저장소에서 함수에 대한 참조를 반환합니다 . 필요에 따라이를 사용하여 조치를 전달할 수 있습니다.
예
import React from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch()
return (
<div>
<span>{value}</span>
<button onClick={() => dispatch({ type: 'increment-counter' })}>
Increment counter
</button>
</div>
)
}
부
dispatch자식 구성 요소에 사용하여 콜백을 전달할 때는이를 참조 로 메모하는 것이 좋습니다. useCallback그렇지 않으면 참조 변경으로 인해 자식 구성 요소가 불필요하게 렌더링 될 수 있습니다.
import React, { useCallback } from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch()
const incrementCounter = useCallback(
() => dispatch({ type: 'increment-counter' }),
[dispatch]
)
return (
<div>
<span>{value}</span>
<MyIncrementButton onIncrement={incrementCounter} />
</div>
)
}
export const MyIncrementButton = React.memo(({ onIncrement }) => (
Increment counter
))