【问题标题】:Do I have to return state in a Redux Reducer?我必须在 Redux Reducer 中返回状态吗?
【发布时间】:2020-06-10 20:57:30
【问题描述】:

我无法理解 redux-reducer 在 JavaScript 中的工作方式。

这是我正在使用的示例:

const intitialState = {
  profile: null,
  profiles: [],
  repos: [],
  loading: true,
  error: {},
};

export default function (state = intitialState, action) {
  const { type, payload } = action;

  switch (type) {
    case GET_PROFILE:
      return {
        ...state,
        profile: payload,
        loading: false,
      };
    case PROFILE_ERROR:
      return {
        ...state,
        error: payload,
        loading: false,
      };
    default:
      return state;
  }
}

关于这个 reducer,以下哪些假设是正确的(如果有的话)?

A) 当 GET_PROFILE 被触发时,它返回 '...state' 作为初始状态,以便 redux 开发工具可以比较状态。

B) 当 GET_PROFILE 被触发时,它实际上只是将值填充到我没有明确列出的对象中。在返回中看起来真的是这样的:

return {
        profiles: profiles,
        repos: repos,
        error: error,
        profile: payload,
        loading: false,
      };

如果 A 或 B 为真,那么如果我不返回 ...state 作为第一个,会发生什么情况,这会弄乱其他对象吗?

编辑:它到底返回到哪里?

【问题讨论】:

  • 你可能会丢失状态的其余部分,因为它被reducer函数返回的内容所取代。

标签: reactjs redux react-redux


【解决方案1】:

我会说 B 是真的。

如果不返回该状态,则该未返回状态的数据将丢失。


createStore 函数为您提供了一个带有 5 个core functions 的商店对象。

我认为从商店返回的以下 3 个函数最值得注意:

  • subscribe 功能 - 用于订阅商店。此函数返回一个unsubscribe 函数用于取消订阅。
    • 如果你使用react-redux,这个订阅/取消订阅逻辑是 已经使用 connect 函数或 useSelector钩子。
  • getState 函数 - 用于获取当前状态。
    • 如果您使用的是react-redux,这也会在connect 函数或useSelector 钩子中处理。
  • dispatch 函数 - 用于更新全局状态。
    • 如果您使用的是react-redux,这是在connect 内部处理的 函数或useDispatch 钩子。

不从减速器返回状态副本将导致您丢失未返回的状态。要了解原因,查看商店的 dispatch 函数会很有帮助。


在 store 的 dispatch 函数内部,您会看到 currentState 变量正在被 reassigned 转换为从减速器(或组合减速器)返回的任何内容。

来自商店simply returns 这个currentState 变量的getState 函数。当前状态总是从这个getState 函数中获取。


因此,当您调度一个操作时,您的状态树将使用您从减速器返回的任何内容进行更新。


这是一个小的普通示例,说明当您从 redux reducer 返回状态与不返回任何状态时会发生什么。

您可以运行并重新运行 sn-p。蓝色按钮调度 GET_PROFILE 并带有返回状态的副本。黄色按钮调度一个动作 GET_PROFILE_NO_STATE_COPY_RETURNED,它返回与 GET_PROFILE 相同的对象,但没有状态副本 ...state

其实,一旦你点击GET_PROFILE_NO_STATE_COPY_RETURNED,你就需要再次运行sn-p,因为没有指定的状态属性完全没有了:)

redux 状态会使用 reducer 返回的任何内容进行更新。

我使用了你问题中的减速器:

const createStore = (reducer, preloadedState, enhancer) => {
  if (typeof enhancer !== 'undefined') {
    return enhancer(createStore)(reducer, preloadedState);
  }
  let currentState = preloadedState;
  let listeners = [];

  const getState = () => currentState;

  const subscribe = listener => {
    listeners.push(listener);
    return () => {
      listeners = listeners.filter(l => l !== listener);
    };
  };

  // The returned from the reducer
  // inside the dispatch function 
  // will be the new state.
  // https://github.com/reduxjs/redux/blob/master/src/createStore.ts#L246
  const dispatch = action => {
    currentState = reducer(currentState, action);
    listeners.forEach(l => l());
  };

  // initialize state
  dispatch({});

  return {
    getState,
    subscribe,
    dispatch,
  };
};

const initialState = {
  profile: null,
  profiles: [],
  repos: [],
  loading: true,
  error: {},
};

// action type constants
const GET_PROFILE = 'GET_PROFILE';
const PROFILE_ERROR = 'PROFILE_ERROR';
const GET_PROFILE_NO_STATE_COPY_RETURNED = 'GET_PROFILE_NO_STATE_COPY_RETURNED';

// reducer
const reducer = (state = initialState, {
  type,
  payload
}) => {
  switch (type) {
    case GET_PROFILE:
      return {
        ...state,
        profile: payload,
        loading: false,
      };
    case GET_PROFILE_NO_STATE_COPY_RETURNED:
      return {
        profile: payload,
        loading: false,
      };
    case PROFILE_ERROR:
      return {
        ...state,
        error: payload,
        loading: false,
      };
    default:
      return state;
  }
};

const store = createStore(reducer);

const render = () => {
  const html = `
    <p>State</p>
    <pre><code>${JSON.stringify(store.getState(), null, 4)}</code></pre>
`;
  document.getElementById('app').innerHTML = html;
};

store.subscribe(render);

render();

document.getElementById('get-profile').addEventListener('click', () => {
  const firstName = document.getElementById('firstName').value;
  const lastName = document.getElementById('lastName').value;
  store.dispatch({
    type: GET_PROFILE,
    payload: {
      firstName,
      lastName,
    },
  });
});

document
  .getElementById('get-profile-no-state-returned')
  .addEventListener('click', () => {
    const firstName = document.getElementById('firstName').value;
    const lastName = document.getElementById('lastName').value;
    store.dispatch({
      type: GET_PROFILE_NO_STATE_COPY_RETURNED,
      payload: {
        firstName,
        lastName,
      },
    });
  });
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet" />

<div id="app"></div>

<input class="form-control" id="firstName" placeholder="firstName" />
<input class="form-control" id="lastName" placeholder="lastName" />
<button class="btn btn-primary" id="get-profile">GET_PROFILE </button>
<button class="btn btn-warning" id="get-profile-no-state-returned">GET_PROFILE_NO_STATE_COPY_RETURNED</button>

因此,当您调度一个操作时,reducer 的返回值将成为您的新状态。

【讨论】:

    【解决方案2】:

    [B] 是最正确的答案,但并不完全准确。

    状态在每个事件(或动作类型)处被替换。 initialState 只与第一步相关。

    如下例所示,当触发 ADD_RECIPIENT 时,行:

    recipients: [...state.recipients, recipient]
    

    表示 state.recipients 是自身加上要添加的新“收件人”的串联。 这是相同的过程:

    return {
        ...state,
        recipients: [...state.recipients, recipient]
    }
    

    state 被自身的串联替换。


    示例:

    const initialState = {
      selectedRecipient: '',
      recipients: []
    };
    
    export default function (state = initialState, action) {
      switch (action.type) {
          case LOAD_RECIPIENTS: {
              const { recipients } = action.payload;
              return {
                  ...state,
                  selectedRecipient: recipients[0],
                  recipients: recipients
              };
          }
          case ADD_RECIPIENT: {
              const { recipient } = action.payload;
              return {
                  ...state,
                  recipients: [...state.recipients, recipient]
              };
          }
    ...
    ...
    

    【讨论】:

      【解决方案3】:

      它像 B 选项一样工作。状态将使用新值更新(正在使用扩展运算符)。然后,新状态被发送到 react(return 将以这种方式工作)react 检查是否有任何更改,并在需要时更新 DOM。非常简化的模型,但接近真实。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-11-10
        • 2020-02-04
        • 1970-01-01
        • 2017-03-26
        • 1970-01-01
        • 1970-01-01
        • 2021-05-03
        相关资源
        最近更新 更多