Почему Redux хочет вернуть кровавый случай, вызванный новым состоянием

React.js

Внутри Редукса (1)

Кровавый случай, вызванный проблемой

В процессе собеседования у блогера интервьюер спросил: «Посмотрите на свое резюме, использовались и Vue, и React, можете ли вы сказать мнеВ чем разница между Vue и React?", а потом сказал это, так! Бывал кровавый случай, когда я отвечал на Vuex и Redux, интервьюер спросил, почему Redux всегда возвращает новыйstate? вернуть старыйstateпочему бы нет ? Излишне говорить, что результаты интервью, ГГ.

вдохновленный

Спустя больше полумесяца, когда я подытожил свой опыт собеседования, я прочитал исходники Redux, ojbk, я запутался, а потом зашел на гитхаб почитать толкования каких-то старших братьев, а потом сам обобщил, написал колонку, используя для дальнейшего изучения

Базовое понимание

Что такое Редукс?

Redux — это контейнер состояния JavaScript, предоставляющий решение для предсказуемого управления состоянием, как описано на официальном сайте:

  // Redux is a predictable state container for JavaScript apps.

Так что же он может сделать?

  // It helps you write applications that behave consistently, run in different environments (client, server, and native) and are easy to test.
  // On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger.

Три принципа

  • Единый источник достоверной информации: для всего приложенияstateхранятся в дереве состояний и существуют только в одном и только одном хранилище

  • состояние доступно только для чтения: единственный способ изменить состояние — запуститьaction, а затем передать действиеtypeА потом отправка отправки. Невозможно напрямую изменить состояние приложения

  • Изменение состояния выполняется чистыми функциями: чтобы описать, как действие изменяет дерево состояний, необходимо написатьreducers

Обрезка

Здесь давайте сначала узнаем о хранилище, промежуточном программном обеспечении, действии, редюсере и т. д.

store

здесьstoreОн генерируется методом createStore(reducers, preloadedState,Enhancer), предоставляемым Redux. Из сигнатуры функции для создания хранилища необходимо передать редукторы, а также можно передать второй необязательный параметр состояния инициализации (preloadedState). Третий параметр вообще промежуточное ПОapplyMiddleware(thunkMiddleware), посмотрите на код, он более интуитивен

  import { createStore, applyMiddleware } from 'redux'
  import thunkMiddleware from 'redux-thunk' // 这里用到了redux-thunk

  const store = createStore(
    reducers,
    state,
    applyMiddleware(thunkMiddleware) // applyMiddleware首先接收thunkMiddleware作为参数,两者组合成为一个新的函数(enhance)
  )

Основной API в редуксе -createStore, хранилище, созданное методом createStore, представляет собой объект, который сам содержит 4 метода:

  • getState(): Получить текущее состояние в магазине.

  • dispatch(action) : отправить действие и вернуть это действие, это единственное, что может изменить данные в хранилище. Способ.

  • subscribe(listener) : зарегистрируйте прослушиватель, который вызывается при изменении хранилища.

  • replaceReducer(nextReducer) : обновите редуктор в текущем хранилище, обычно вызывайте этот метод только в режиме разработки.

middleware

На рисунке ниже показан простой сценарий синхронного потока данных в Redux.После нажатия кнопки в обратном вызове отправляется действие.После получения действия редюсер обновляет состояние и уведомляет представление о повторной отрисовке.

Односторонний поток данных, кажется, что нет никаких проблем. Однако, если вам нужно распечатать информацию о каждом действии для отладки, вы должны изменить реализацию диспетчера или редуктора, чтобы иметь функцию печати журналов.

Для другого примера, после нажатия кнопки вам нужно перейти к серверу, чтобы сначала запросить данные, и только после того, как данные будут возвращены, можно повторно отобразить представление, В это время мы надеемся, что диспетчеризация или редьюсер имеет функцию асинхронный запрос. Другой пример: после возврата данных асинхронного запроса печатается журнал, затем запрашиваются данные, печатается журнал, а затем отображается.

Перед лицом различных бизнес-сценариев, очевидно, не универсально просто модифицировать код диспетчеризации или редуктора.Redux опирается на идею промежуточного программного обеспечения в Node.js Koa.Редуктор в Redux больше связан с логикой преобразования данных , такПромежуточное ПО улучшает диспетчеризацию.

Action

  // 引用官网的介绍

  // Actions are payloads of information that send data from your application to your store. 
  // They are the only source of information for the store
  // You send them to the store using store.dispatch().

Действия — это полезные данные, которые передают данные из приложения в магазин. Это единственный источник данных магазина. Проще говоря, Action — это тип сообщения, которое сообщает Redux, что делать, когда пришло время это сделать, и передает его в Redux с соответствующими данными.

Действие — это простой объект, который должен иметь атрибут типа, который используется для обозначения типа действия (редьюсер определяет выполняемую логику), а другие атрибуты могут быть настроены пользователем. Такие как:

  const START_FETCH_API = 'START_FETCH_API'
  {
    type: START_FETCH_API,
    data: {
      id: itemId,
      value: 'I am Value'
    }
  }

Action Creator

Взгляните на введение на официальном веб-сайте: Action Creator — это именно то, что нужно — функции, которые создают действия Легко спутать термины «действие» и «создатель действия», поэтому постарайтесь использовать правильный термин. То есть: Action Creator в Redux просто возвращает действие.

  function fetchStartRequestApi(jsondata) {
    return {
      type: START_FETCH_API,
      data: jsondata
    }
  }

Мы знаем, что Redux развился из Flux.В традиционном Flux создатели действий часто запускают отправку после вызова. Например:

  function fetchStartRequestApiDispatch(jsondata) {
    const action = {
      type: START_FETCH_API,
      data: jsondata
    }
    dispatch(action)
  }

Однако в Redux нам нужно только передать результат, возвращенный создателями действий, в dispatch(), чтобы завершить процесс инициации отправки или даже создать被绑定的 Action Creatorsавтоматически отправлять

  // example 1
  dispatch(fetchStartRequestApi(jsondata))

  // example 2
  const boundFetchStartRequestApiDispatch = jsondata => dispatch(fetchStartRequestApi(jsondata))

Кто-то здесь вот-вот упадет в обморок, что такое dispatch()? На самом деле, как упоминалось ранее, создается с помощью createStore().storeObject, у него есть метод: dispatch(action), метод dispatch() можно вызвать прямо в магазине через store.dispatch(), но в большинстве случаев мы будем использовать хелпер connect(), предоставленный react-redux для вызов. bindActionCreators() может автоматически привязывать несколько создателей действий к методу dispatch().

Reducers

  // 引用官网的介绍

  // Reducers specify how the application's state changes in response to actions sent to the store. 
  // Remember that actions only describe what happened, but don't describe how the application's state changes

Как упоминалось выше, Reducers должны быть чистой функцией, которая обрабатывает обновления состояния в соответствии с действиями.Если обновления нет или встречается неизвестное действие, она возвращает старое состояние, в противном случае — новый объект состояния. __Примечание. Вы не можете изменить старое состояние, вы должны сначала скопировать состояние, а затем изменить его, или вы можете использовать функцию Object.assign для создания нового состояния. __Конкретная причина, которую мы узнаем, когда читаем исходный код~

Никогда не выполняйте эти операции в редукторах:

  • Изменить входящие параметры;

  • Выполнение операций с побочными эффектами, таких как запросы API и переходы по маршруту;

  • вызывать нечистые функции, такие как Date.now() или Math.random();

Последний пример кода ниже помогает переварить, отправить запрос и получить список музыки.

  // action.js

  /*
   * action 类型
  */  
  export const START_FETCH_API = 'START_FETCH_API'
  export const STOP_FETCH_API = 'STOP_FETCH_API'
  export const RECEIVE_DATA_LIST = 'RECEIVE_DATA_LIST'
  export const SET_OTHER_FILTERS = 'SET_OTHER_FILTERS'

  /*
   * 其它的常量
  */
  export const otherFilters = {
    SHOW_ALL: 'SHOW_ALL',
    SHOW_ACTIVE: 'SHOW_ACTIVE'
  }

  /*
   * action 创建函数
  */
  export function startFetchApi() {
    return {
      type: START_FETCH_API
    }
  }

  export function stopFetchApi() {
    return {
      type: STOP_FETCH_API
    }
  }

  export function receiveApi(jsondata) {
    return {
      type: RECEIVE_DATA_LIST,
      data: jsondata
    }
  }

  export function setOtherFilters (filter) {
    return {
      type: SET_OTHER_FILTERS,
      data: filter
    }
  }

  // 异步
  export const fetchMusicListApi = (music_id) => dispatch => {
    dispatch(startFetchApi())
    fetch({
      url: url,
      method: 'POST',
      data: {
        music_id: music_id
      }
    }).then((res) => {
      dispatch(stopFetchApi())
      dispatch(receiveApi())
    }).catch((err) => {
      console.log(err)
    })
  }
  // reducers.js
  
  // 引入 action.js
  import { otherFilters } from './action'

  // 初始 state
  const initialState = {
    otherFilters: otherFilters.SHOW_ALL,
    list: [],
    isFetching: false
  }

  function reducers(state, action) {
    switch(action.type) {
      case SET_OTHER_FILTERS: 
        return Object.assign({}, state, {
          otherFilters: action.payload.data
        })
      case START_FETCH_API:
        return Object.assign({}, state, {
          isFetching: true
        })
      case STOP_FETCH_API:
        return Object.assign({}, state, {
          isFetching: false
        })
      case RECEIVE_DATA_LIST:
        return Object.assign({}, state, {
          list: [ ...action.payload.data ]
        })
      default: 
        return state
    }
  }

Уведомление

  1. Не изменять состояние. использоватьObject.assign()Создается новая копия. Object.assign(state, {otherFilters: action.payload.data}) нельзя использовать таким образом, потому что это изменит значение первого параметра. Вы должны установить первый параметр на пустой объект

  2. Возвращает старое состояние в случае по умолчанию. Столкнувшись с неизвестным действием, обязательно вернитесь к старому состоянию.

Сомнение в уме

  • Как bindActionCreators() автоматически помогает мне привязать действия к отправке?

  • Что такое чистая функция?

  • Почему редюсер должен быть чистой функцией?

  • Почему можно изменить состояние только через действие, в чем проблема с прямым изменением?

  • Как bindActionCreators() автоматически помогает мне привязать действия к отправке?

  • Почему редюсеры должны возвращать старое состояние в случае дефолта?

  • ...

Продолжение следует

Я еще не дочитал, а там еще следующее, просто хочу еще немного разобраться, а потом продолжить писать конспект, цель прочитать самому, в том числе и новичкам, надеюсь покажете милосердие, вы можете пропустить, если вам не интересно~, перейдите прямо на github, чтобы увидеть исходный код, портал находится здесь:Redux


Обновление 2019.01.03, здесь следующее:«Почему редукс возвращается к кровавому делу, вызванному новым состоянием (2)»

Ссылки по теме