Redux
  • Read Me
  • 소개
    • 동기
    • Core Concepts
    • 3가지 원칙
    • 기존 기술들
    • Learning Resources
    • 생태계
    • 예시
  • 기초
    • 액션
    • 리듀서
    • 스토어
    • 데이터 흐름
    • React와 함께 사용하기
    • 예시: Todo List
  • 심화
    • 비동기 액션
    • 비동기 흐름
    • 미들웨어
    • React Router와 함께 사용하기
    • 예시: Reddit API
    • Next Steps
  • 레시피
    • Configuring Your Store
    • Redux로 마이그레이션
    • 객체 확산 연산자 사용하기
    • 보일러플레이트 줄이기
    • Server Rendering
    • Writing Tests
    • Computing Derived Data
    • Implementing Undo History
    • Isolating Subapps
    • 리듀서 구조화하기
      • 사전에 요구되는 개념들
      • 기본 리듀서 구조
      • 리듀서 로직 분리하기
      • 리듀서 예제 리팩토링하기
      • combineReducers 사용하기
      • combineReducers 더 알아보기
      • 상태 정규화하기
      • 정규화된 데이터 업데이트하기
      • 리듀서 로직 재사용하기
      • 불변 업데이트 패턴
      • 상태 초기화하기
    • Using Immutable.JS with Redux
  • FAQ
    • General
    • Reducers
    • Organizing State
    • Store Setup
    • Actions
    • Immutable Data
    • Code Structure
    • Performance
    • Design Decisions
    • React Redux
    • Miscellaneous
  • 문제해결
  • 용어사전
  • API 레퍼런스
    • createStore
    • Store
    • combineReducers
    • applyMiddleware
    • bindActionCreators
    • compose
  • 변경 기록
  • 후원자
  • 피드백
Powered by GitBook
On this page
  • 액션을 디스패치했는데 아무 일도 일어나지 않는다
  • 다른것이 작동하지 않습니다

문제해결

PreviousMiscellaneousNext용어사전

Last updated 6 years ago

여기는 일반적인 문제들과 그 해결책들을 공유하는 곳입니다. 예시는 React를 사용하지만, 다른 뭔가를 쓰더라도 유용할겁니다.

액션을 디스패치했는데 아무 일도 일어나지 않는다

액션을 디스패치하려 했지만 뷰가 바뀌지 않는 경우가 있습니다. 왜 그럴까요? 몇 가지 이유가 있을 수 있습니다.

리듀서의 인수들을 절대 변경(mutate)하지 마세요

Redux가 전달한 state나 action을 수정하고 싶어질 때가 있습니다. 그러지 마세요!

Redux는 리듀서에 주어진 객체를 절대 변경하지 않을 것이라고 가정합니다. 언제나 새 상태 객체를 반환하세요. 같은 라이브러리를 사용하지 않더라도, 변경은 언제나 피해야 합니다.

불변성은 가 잘 다듬어진 업데이트를 구독하도록 해줍니다. 을 통한 시간여행같은 멋진 개발자 기능도 가능하게 해주고요.

예를 들어 아래와 같은 리듀서는 상태를 변경하기 때문에 틀렸습니다:

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      // Wrong! This mutates state
      state.push({
        text: action.text,
        completed: false
      })
      return state
    case 'COMPLETE_TODO':
      // Wrong! This mutates state[action.index].
      state[action.index].completed = true
      return state
    default:
      return state
  }
}

이렇게 고쳐야 합니다:

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      // Return a new array
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case 'COMPLETE_TODO':
      // Return a new array
      return state.map((todo, index) => {
        if (index === action.index) {
          // Copy the object before mutating
          return Object.assign({}, todo, {
            completed: true
          })
        }
        return todo
      })
    default:
      return state
  }
}
// Before:
return state.map((todo, index) => {
  if (index === action.index) {
    return Object.assign({}, todo, {
      completed: true
    })
  }
  return todo
})

// After
return update(state, {
    completed: {
      $set: true
    }
  }
})

Object.assign을 올바르게 사용하도록 하세요. 예를 들어 리듀서에서 Object.assign(state, newData) 처럼 반환하는 대신 Object.assign({}, state, newData)처럼 반환해야 합니다. 이렇게 하면 이전의 state를 덮어쓰지 않게 됩니다.

// Before:
return state.map((todo, index) => {
  if (index === action.index) {
    return Object.assign({}, todo, {
      completed: true
    })
  }
  return todo
})

// After:
return state.map((todo, index) => {
  if (index === action.index) {
    return { ...todo, completed: true }
  }
  return todo
})

이 실험적인 언어 기능은 변경될 수 있음을 알아두세요.

액션 생산자를 정의했다면, 이걸 호출한다고 자동으로 액션이 디스패치되지 않습니다. 예를 들어 이 코드는 아무것도 하지 않습니다:

TodoActions.js

export function addTodo(text) {
  return { type: 'ADD_TODO', text }
}

AddTodo.js

import React, { Component } from 'react'
import { addTodo } from './TodoActions'

class AddTodo extends Component {
  handleClick() {
    // Won't work!
    addTodo('Fix the issue')
  }

  render() {
    return (
      <button onClick={() => this.handleClick()}>
        Add
      </button>
    )
  }
}

액션 생산자는 단지 액션을 반환하는 함수일 뿐이기 때문에 이 코드는 작동하지 않습니다. 실제로 디스패치하는 것은 당신에게 달렸습니다. 서버에서 랜더링되는 앱의 경우 매 요청마다 별개의 스토어를 요구하기 때문에 액션 생산자를 정의하는 동안 특정 스토어 인스턴스에 바인드 할 수는 없습니다.

handleClick() {
  // Works! (but you need to grab store somehow)
  store.dispatch(addTodo('Fix the issue'))
}

수정된 코드는 아래와 같습니다:

AddTodo.js

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { addTodo } from './TodoActions'

class AddTodo extends Component {
  handleClick() {
    // Works!
    this.props.dispatch(addTodo('Fix the issue'))
  }

  render() {
    return (
      <button onClick={() => this.handleClick()}>
        Add
      </button>
    )
  }
}

// In addition to the state, `connect` puts `dispatch` in our props.
export default connect()(AddTodo)

이제 원한다면 dispatch를 다른 컴포넌트로 직접 내려줄 수도 있습니다.

mapStateToProps가 정확한지 보세요

액션을 제대로 디스패치했고 리듀서를 잘 적용했지만 그에 대응하는 상태가 props로 제대로 전달되지 않았을 수도 있습니다.

다른것이 작동하지 않습니다

코드는 더 많지만 이게 바로 Redux를 예측 가능하고 능률적으로 만들어줍니다. 코드를 더 줄이고 싶다면 과 같은 헬퍼를 써서 간단한 문법으로 불변형 변환을 작성할 수 있습니다:

마지막으로, 객체를 업데이트하기 위해 Underscore의 _.extend 같은 것들이나, 더 좋은 폴리필 등을 사용할 수 있습니다.

더 간결한 문법을 위해 을 사용할 수도 있습니다:

중첩된 상태 객체가 깊게 복사되도록 주의하세요. _.extend와 Object.assign 모두 상태를 얕게 복사합니다. 중첩된 상태를 어떻게 다룰지에 대한 제안은 에서 볼 수 있습니다.

를 호출하는걸 잊지 마세요

인스턴스의 메서드를 호출해서 고칠 수 있습니다:

컴포넌트 계층의 깊은 곳까지 스토어를 전달하는 것은 번거로운 일입니다. 가 connect 를 써서 컴포넌트의 props에 dispatch를 주입해주는 이유입니다.

#redux Discord 채널에서 물어보거나, 하세요. 문제를 해결했다면, 같은 문제를 가진 다른 사람들를 위해 해주세요.

Immutable
react-redux
redux-devtools
React.addons.update
Object.assign
object spread operator proposal
중첩된 객체 업데이트하기
react-redux
고차 컴포넌트
Reactiflux
이슈를 생성
이 문서를 수정
store
dispatch(action)
dispatch()