분류 Reactjs

Redux (및 React-Redux)에 대한 유일한 소개 (2)

컨텐츠 정보

  • 조회 299 (작성일 )

본문

Actions 


이제 무엇을 할지 알고 있으므로 메모 추가를 위한 작업을 시작하겠습니다.

actions.js 내부에서 초기 코드를 추가하겠습니다.


export const ADD_NOTE = 'ADD_NOTE';
export function addNote(title, content) {
return { type: ADD_NOTE, title: title, content: content };
}


진행 상황을 설명하기 위해 한줄씩 살펴 보겠습니다.

  1. 나중에 여러 곳에서 필요하기 때문에 상수 ADD_NOTE를 내보내고 있습니다. 예, 우리는 'ADD_NOTE'라는 문자열을 쓰면서 어디든 갈 수 있었지만 그것은 나쁜 습관입니다! 프로젝트가 엄청나게 커지고 상사가“ADD_NOTE 액션의 이름을 ADD_NEW_NOTE로 바꾸십시오”라고 말합니다. 내가 어디로 가는지 봤니?
  2. addNote 함수를 내보내고 있습니다. 앞에서 살펴본 바와 같이 이 함수는 액션 제작자입니다. 즉, 해당 작업은 일반 객체 만 반환하는 것입니다. 그리고 어떤 논리도 하지 않는 것을 주목하십시오. 단지 객체를 반환합니다. 우리가 이전에 말했 듯이, 행동은 어떻게 변화 했는지를 정의합니다.

이것은 행동에 관한 Redux의 관습입니다. 

조금 이상하게 보일지 모르지만 상수와 액션 크리에이터를 내보내는 것이 가장 논리적인 방법입니다.


Reducers 


상점을 작성하고 조치를 발송하기 전에 먼저 기본 (루트) 감속기를 작성하십시오.


import { ADD_NOTE } from '../actions/actions';
const initialState = {
notes: []
};
function rootReducer(state = initialState, action) {
switch(action.type) {
case ADD_NOTE:
return {
notes: [
...state.notes,
{
title: action.title,
content: action.content
}
]
};
default:
return state;
};
}
export default rootReducer;


보시다시피, 우리는 리듀서의 초기 상태 객체를 선언하고 있습니다.이 경우 앱에서 유일한 리듀서이기 때문에 전체 애플리케이션의 상태로 밝혀졌습니다.


앞에서 말했듯이 감속기는 이전 상태와 디스패치중인 2 개의 매개 변수를 받습니다. 우리는 액션 유형이 무엇인지 확인하고 그 값에 따라 새로운 상태를 반환합니다.


상태를 직접 수정하지 마십시오! 우리는 모든 감속기를 불변으로 유지하고 싶습니다! 우리의 모든 사례가 소진 된 후에 우리는 단순히 앱 상태를 반환합니다 (나중에 더 자세히 설명하지만 Redux 초기화와 관련이 있습니다).


노트 :


case ADD_NOTE:
return {
notes: [
...state.notes,
{
title: action.title,
content: action.content
}
]
}; 


여기서는 앱의 새로운 상태 객체 전체를 반환하고 있으며 notes 속성은 새로운 상태의 유일한 속성이라고 가정합니다. 이전 상태에 다른 속성이 있으면 유지하지 않기 때문에 손실 될 수 있습니다.


다른 속성이 있고 새 속성에서 제외하지 않으려는 경우 노트 속성 앞이나 뒤에 스프레드 연산자를 사용하십시오.


case ADD_NOTE:
return {
...states, // <- like so
notes: [
...state.notes,
{
title: action.title,
content: action.content
}
]
}; 


한 가지 더 : switch 문으로 인해 눈이 아프면 if 문도 좋습니다. 그러나 종종 스위치 문에 도달합니다.


Store 


이제 앱 스토어를 만들어 봅시다!


import { createStore } from 'redux';
import rootReducer from '../reducers/reducers';
export default createStore(rootReducer);


Redux에는 이 createStore 기능이 있습니다.

이 기능은 매우 간단하고 설명이 필요 없으며 응용 프로그램의 저장소를 만드는 데 사용됩니다. 

3 개의 인수를 사용할 수 있지만 (마지막 2 개는 선택 사항 임) 지금은 응용 프로그램의 루트 감속기를 허용하는 첫 번째 인수 만 사용합니다.


다음으로 main.js 파일에서 상점을 가져 와서 몇 가지 메모를 추가하기 전후의 상태를 확인하기 위해 몇 가지 작업을 전달합니다.


import store from './store/store';
import { addNote } from './actions/actions';
// We use store.getState() to get our app state from the store
console.log('Before:', store.getState());
store.dispatch(addNote('One', 'One content'));
store.dispatch(addNote('Two', 'Two content'));
store.dispatch(addNote('Three', 'Three content'));
console.log('After:', store.getState());


1*X98aunelHCCSvQPaKm6LwA.png 


메모 추가 


새 메모를 추가하려면 양식 이벤트 핸들러에 3 개의 새 줄을 추가하면 됩니다.


addNoteForm.addEventListener('submit', (e) => {
e.preventDefault();
let title = addNoteTitle.value;
let content = addNoteContent.value;
store.dispatch(addNote(title, content));
});


우리가 하는 일은 단순히 우리 가게에 행동을 파견하는 것입니다! 

우리의 action creator 객체는 우리가 공급하는 제목과 내용 값을 가진 객체를 반환하는데, 이것은 실제로 양식 필드에 넣는 내용입니다.


랜더링 노트 


앞에서 언급 했듯이 Redux의 저장소는 객체입니다. 여기에는 subscribe라는 함수가 있는데, 상태 트리의 변경 사항을 구독하는 데 사용할 수 있습니다. subscribe 메소드를 사용하여 구독을 사용하여 메모를 다시 렌더링 합니다.


참고 : 순서가 없는 목록에 있는 모든 메모를 다시 렌더링 합니다. 반대로 React, Angular 또는 Vue와 같은 프레임 워크 / 라이브러리를 사용하는 경우 업데이트해야 할 항목을 계산하고 직접 또는 심지어 수행 해야 하는 번거로움을 덜어주는 최적화 된 diffing 알고리즘이 있을 것입니다. 우리처럼 DOM에서 완전히 다시 렌더링 합니다.


먼저 renderNotes 함수를 업데이트하겠습니다.


function renderNotes() {
let notes = store.getState().notes;
notesUList.innerHTML = '';
notes.map((note, index) => {
let noteItem = `
<li>
<b>${ note.title }</b>
<button data-id="${ index }">x</button>
<br />
<span>${ note.content }</span>
</li>
`;
notesUList.innerHTML += noteItem;
});
setDeleteNoteButtonsEventListeners();
}


이제 store을 구독하겠습니다.


store.subscribe(() => {
renderNotes();
});



이제 페이지로 돌아가서 새로운 메모를 추가하십시오! 마술 같지 않습니까?!


store에서 구독 취소 


어떤 이유로 상점에서 구독을 취소하려면 store.subscribe 함수가 함수를 리턴 합니다. 따라서 해당 함수를 호출하여 상점에서 구독을 취소합니다.


const unsubscribe = store.subscribe(() => {
renderNotes();
});
unsubscribe();
// Adding new notes won’t trigger a change in the UI now


메모 제거 


먼저 actions.js 파일로 이동하여 새로운 상수와 액션 생성자를 추가합니다.


export const REMOVE_NOTE = 'REMOVE_NOTE';
export function removeNote(id) {
return { type: REMOVE_NOTE, id: id };
}


다음으로 reducers.js 파일로 이동하여 switch 문에 새 사례를 추가합니다 (REMOVE_NOTE도 가져와야 함).


case REMOVE_NOTE:
return {
notes: state.notes.filter((note, index) => index != action.id)
};


마지막으로 main.js에서 deleteNote 함수를 업데이트합니다.


function deleteNote(index) {
store.dispatch(removeNote(index));
}


이제 사용자는 메모를 삭제할 수 있습니다. 파이만큼 쉽습니다.


초기화 상태 


앱 상태를 초기화하기 전에 감속기를 다시 한 번 살펴보고 현재 상황을 이해하는 것이 좋습니다.


1) 각 리듀서에는 자체 상태가 있으며 앱 상태와 다를 수 있습니다 (두 개 이상의 리듀서가 있는 경우).

이제 하나의 리듀서 (루트 리듀서) 만 있기 때문에 결국 앱 상태가 됩니다.


2) Redux가 앱을 초기화하면 switch 문에서 사례를 충족시키지 않는 작업을 보냅니다.

실제로 콘솔에 작업 유형을 기록하면 페이지를 처음 방문 할 때 얻을 수 있는 작업입니다.


1*CDr1pSKCLU3M5CauF4ZIWQ.png 


앱의 초기 상태를 제공하지 않았으므로 상태는 정의되지 않으므로 기본적으로 initialState 객체로 돌아갑니다.


3) 간단한 노트 / 할 일 응용 프로그램보다 더 복잡하기 때문에 앱에 둘 이상의 감속기가 있을 것입니다. 이 경우 여러 리듀서를 작성하게 됩니다.


Redux는 여러 리듀서를 결합하여 마치 하나의 리듀서 인 것처럼 createStore 함수에 전달할 수 있는 combineReducers라는 함수를 제공합니다.


흥미로운 부분은 다음과 같습니다. 

combineReducers 함수 안에서 리듀서의 객체를 전달합니다. 각 리듀서는 자체 애플리케이션 상태 부분을 처리합니다 (이제 리듀서는 앱 상태 전체를 신경 쓰지 않습니다). 그런 다음 Redux는 객체의 모든 키와 값에서 응용 프로그램의 상태를 만듭니다.


먼저, src/reducers 디렉토리 안에 새로운 파일 notesReducer.js를 크레이트하여 src/reducers / reducers.js 파일에서 코드를 옮기고 약간 수정합니다 :


import { ADD_NOTE, REMOVE_NOTE } from '../actions/actions';
function notesReducer(notes = [], action) {
switch(action.type) {
case ADD_NOTE:
return [
...notes,
{
title: action.title,
content: action.content
}
];
case REMOVE_NOTE:
return notes.filter((note, index) => index != action.id);
default:
return notes;
};
}
export default notesReducer;


참고 : 첫 번째 매개 변수를 state에서 notes로 변경했습니다. 감속기는 노트 만 관리하므로 항상 배열 만 반환합니다. 다른 상태는 감속기에서 전달되지 않습니다!


다음으로 또 다른 감속기를 추가하겠습니다. 그것을 가시성 필터라고 합시다. 가시성별로 메모를 필터링 합니다.

참고 :이 기사는 실제로 커지기 때문에 이 부분의 UI를 작성하거나 가시성 필터 기능을 확장하지 않습니다. 숙제가 될 것입니다!

힌트 : 가시성 필터를 사용하려면 메모 개체에 태그 필드를 추가하는 것이 좋습니다. 그런 다음 몇 가지 태그 (예 : 보관 및 중요)를 추가하고 단일 메모의 태그를 변경하기 위한 작업을 추가하십시오. 마지막으로 보관, 중요 또는 모든 메모로 태그가 지정된 메모 만 표시하는 데 도움이 되는 select 요소를 추가하십시오.


import { SHOW_ALL } from '../actions/actions';
function visibilityFilter(visibility = SHOW_ALL, action) {
switch(action.type) {
case SHOW_ALL:
return SHOW_ALL;
default:
return visibility;
};
}
export default visibilityFilter;


상태의 가시성 부분 만 처리하는 방법에 주목하고 애플리케이션 상태의 노트 부분이나 전체 앱 상태에 관심이 없습니까? 그렇기 때문에 Redux는 매우 강력하고 굉장합니다!


actions.js 파일에서 다음 내용을 삽입하십시오.


export const SHOW_ALL = 'SHOW_ALL';
export function showAll() {
return { type: SHOW_ALL };
}


이제 reducers.js 파일에서 감속기를 결합 할 것입니다.


import notesReducer from './notesReducer';
import visibilityFilter from './visibilityFilter';
import { combineReducers } from 'redux';
const reducers = combineReducers({
notes: notesReducer,
visibility: visibilityFilter
});
export default reducers;


마지막으로 store.js 파일을 보다 구체적으로 다시 작성해 보겠습니다.


import { createStore } from 'redux';
import reducers from '../reducers/reducers';
export default createStore(reducers);


이제 주 응용 프로그램을 main.js 파일에 기록하려고 하면 다음과 같은 결과가 나타납니다.


1*3j1CncWMX1M6FvrgUeW5Ew.png 


우리 상태는 combineReducers 함수에서 제공 한 키를 가진 객체로 끝났고 각 감속기는 자체 상태를 관리합니다.


4) createStore 함수의 두 번째 매개 변수로 응용 프로그램의 초기 상태를 제공 할 수 있습니다. 감속기의 모든 기본값을 덮어 씁니다.


1*sm5zmq1J3PHqTa4iqwC04A.png 


예를 들어 이전 앱 상태의 사본을 로컬 저장소에 보관 한 경우 두 번째 매개 변수로 제공하기 전에 값을 얻었습니다. 그러나 두 번째 매개 변수 동기 데이터 만 제공해야 합니다 (로컬 스토리지 API는 동기식이므로 문제가 없습니다).


"알겠습니다.하지만 대신 API에서 초기 상태를 로드 하려면 어떻게 해야 합니까?".


이 경우 상태의 비동기 초기화를 원합니다. 루트 구성 요소에서 React (또는 다른 라이브러리 / 프레임 워크를 사용하는 경우 동등한 수명주기 이벤트)를 사용하는 경우 componentDidMount ()에서 API 호출을 수행 한 다음 앱의 초기 상태 설정을 위한 작업을 디스패치 하십시오 (또는 여러 작업을 디스패치 하십시오) 대신-원하는 대로). 이 패키지도 살펴볼 수도 있습니다.


앱을 Redux DevTools 브라우저 확장 프로그램과 연결 


Redux DevTools 확장은 매우 강력한 도구입니다. 

이 기사의 시작 부분에서 약간 압도적으로 느껴질 것이기 때문에 준비가 될 때까지 이 기능을 사용하고 싶지는 않았습니다.


어쨌든 이 줄을 createStore 함수의 세 번째 매개 변수로 추가하십시오.

 

window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() 


이것이 store.js 파일의 모습입니다 :


import { createStore } from 'redux';
import reducers from '../reducers/reducers';
let initialState = {
notes: [
{ title: 'You are awesome', content: 'No, wait, I meant legendary!' },
{ title: 'Ooops', content: 'I was talking to myself' }
],
visibility: 'AWESOME_TAG'
};
export default createStore(
reducers,
initialState,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);


애플리케이션의 초기 상태를 제공하지 않는 경우 undefined를 두 번째 매개 변수로 전달하면 됩니다.


1*6H95WqZuf9_fkrWaOEIJuQ.png 


그러나 보다 복잡한 앱의 경우 Redux DevTools 확장 문서를 확인하십시오.


createStore 함수의 세 번째 매개 변수는 Redux에 미들웨어와 같은 인핸서를 배치하는 위치입니다. 이 기사의 범위를 벗어 났으므로 이미 충분히 길었으므로 Redux 문서의 고급 섹션을 방문하기를 간절히 바랍니다.


이것이 첫 번째 프로젝트에 관한 것입니다. 여기 GitHub에서 찾을 수 있습니다.