wake-up-neo.com

Der richtige Weg, um den Status in den Reduktionsminderern zu aktualisieren

Ich bin ein Neuling in Redux und ES6-Syntax. Ich mache meine App mit offiziellem Redux-Tutorial und mit diesem Beispiel

Es gibt JS-Ausschnitt unten. Mein Punkt - um die Fälle REQUEST_POST_BODY und RECEIVE_POST_BODY im Beitragsreduzierer zu definieren. Hauptsächlich schwierig - das richtige Objekt im Laden zu finden und zu aktualisieren.

Ich versuche, Code aus Beispiel zu verwenden:

  return Object.assign({}, state, {
    [action.subreddit]: posts(state[action.subreddit], action)
  })

Aber es wurden einfache Beiträge verwendet. Es ist nicht erforderlich, den richtigen Beitrag anhand der ID zu finden.

Hier mein Code:

  const initialState = {
    items: [{id:3, title: '1984', isFetching:false}, {id:6, title: 'Mouse', isFetching:false}]
  }

  // Reducer for posts store
  export default function posts(state = initialState, action) {
    switch (action.type) {
    case REQUEST_POST_BODY:
      // here I need to set post.isFetching => true
    case RECEIVE_POST_BODY:
      // here I need to set post.isFetching => false and post.body => action.body
    default:
      return state;
    }
  }

  function requestPostBody(id) {
    return {
      type: REQUEST_POST_BODY,
      id
    };
  }

  function receivePostBody(id, body_from_server) {
    return {
      type: RECEIVE_POST_BODY,
      id,
      body: body_from_server
    };
  }

  dispatch(requestPostBody(3));
  dispatch(receivePostBody(3, {id:3, body: 'blablabla'}));
14
Max Vorozhcov

Mit Arrays

Wenn Sie lieber mit Arrays bleiben möchten, können Sie einen Reduzierer schreiben, der nur einzelne post-Objekte behandelt.

export default function reducePost(post, action) {
  if(post.id !== action.id) return post;

  switch(action.type) {
  case REQUEST_POST_BODY:
    return Object.assign({}, post, { isFetching: true });
  case RECEIVE_POST_BODY:
    return Object.assign({}, post, { isFetching: false, body: action.body });
  default:
    return post;
}

Ihr Wurzelreduzierer würde zu:

export default function posts(state = initialState, action) {
  return state.map(post => reducePost(post, action);
}

Wir führen unseren neuen Reducer nur für jeden Post in der Liste aus, um ein aktualisiertes Array von Posts zurückzugeben. In diesem Fall sorgt die eindeutige ID dafür, dass nur ein Element geändert wird.

Mit Objekten

Wenn jedes Element eine eindeutige Zeichenfolgen-/Nummern-ID hat, können Sie Ihr Array umdrehen und stattdessen eine object verwenden.

const initialState = {
  items: {
    3: {id:3, title: '1984', isFetching:false},
    6: {id:6, title: 'Mouse', isFetching:false}
  };
}

Dann können Sie Ihren Reduzierer vereinfachen.

switch (action.type) {
case REQUEST_POST_BODY:
  let id = action.id;
  return Object.assign({}, state, {
    [id]: Object.assign({}, state[id], { isFetching: true })
  });
case RECEIVE_POST_BODY:
  let id = action.id;
  return Object.assign({}, state, {
    [id]: Object.assign({}, state[id], {
      isFetching: false,
      body: action.body
    })
  });
default:
  return state;
}

Wenn Sie auch gerne mit etwas ES7-Syntax experimentieren, können Sie den Object-Spread-Operator mit Babel aktivieren und die Aufrufe in Object.assign umschreiben.

switch (action.type) {
case REQUEST_POST_BODY:
  let id = action.id;
  return {
    ...state,
    [id]: {...state[id], isFetching: true }
  };
case RECEIVE_POST_BODY:
  let id = action.id;
  return {
    ...state,
    [id]: {
      ...state[id],
      isFetching: false,
      body: action.body
    }
  };
default:
  return state;
}

Wenn Sie die Spread-Syntax nicht so gern verwenden, können Sie Object.assign noch etwas schmackhafter machen.

function $set(...objects) {
  return Object.assign({}, ...objects); 
}
case RECEIVE_POST_BODY:
  let id = action.id;
  return $set(state, {
    [id]: $set(state[id], {
      isFetching: false,
      body: action.body
    })
  });
24
Dan Prince

Wenn ich es richtig verstehe, haben Sie Probleme, den gewünschten Beitrag zu erhalten.

Zunächst einmal: Wenn Sie das Array und das Objekt in Ihrem Reducer auch aktualisieren, ist es schwierig zu lesen und zu warten. Ich schlage vor, Sie schauen sich dieses kurzes Video an, in dem Sie die Zusammensetzung des Reduzierers mit Arrays erläutern ... Sie können Ihren Code durch Verwendung der dort beschriebenen Technik vereinfachen.

In Ihrem Fall würden Sie einen posts-Reduzierer und einen post-Reduzierer verwenden, während posts-Reduzierer den post-Reduzierer aufruft.

Wenn Sie das richtige Objekt suchen, an dem Sie arbeiten können, macht es Dan Prince mit seinem Vorschlag einfacher. Eine Objekt-Map anstelle eines Arrays würde Ihnen die Arbeit erleichtern. Relevantes Code-Snippet aus Dans Antwort:

const initialState = {
  items: {
    3: {id:3, title: '1984', isFetching:false},
    6: {id:6, title: 'Mouse', isFetching:false}
  ];
}
1
Moti Azu

Ich habe so ziemlich Objektreduzierer implementiert, indem ich Object.assign, was funktioniert, aber da unser Projekt gewachsen ist und wir eine Reihe von abhängigen Komponenten hinzugefügt haben, ist es sehr ineffizient geworden und das Rendern ist sehr langsam.

Wenn ich von immer gewusst hätte, hätte ich das von Anfang an verwendet.

Grundsätzlich verwenden Sie immer wie folgt, wobei das Beispiel ein layers -Objekt hat, das so aussieht:

const initialState = {
  layers: {
   'layer1': { selected: false },
   'layer2': { selected: true }
  }
}

Reducers.js (Auszug)

import produce from 'immer'
import has from 'lodash/has'
export const layers = (state = null, action) => {
  switch (action.type) {
    case ADD_LAYER:
      // Using `immer.produce` only for consistency 
      // clearly the whole object changes in this case.
      return produce(state, layers => {
        // Take a copy of the prebuilt layer
        var layer = Object.assign({}, action.layer)
        // Add some defaults
        if (!has(layer, 'selected')) {
          layer.selected = false
        }
        layers[action.id] = layer
      })
    case SELECT_LAYER:
      return produce(state, layers => {
        layers[action.id].selected = true
      })
    case UNSELECT_LAYER:
      return produce(state, layers => {
        layers[action.id].selected = false
      })
    default:
      return state
  }
}

Actions.js (Auszug)

export const addLayer = id => ({
  type: ADD_LAYER,
  id
})

export const selectLayer = id => ({
  type: SELECT_LAYER,
  id
})

export const unSelectLayer = id => ({
  type: UNSELECT_LAYER,
  id
})

Verweise:

https://github.com/immerjs/immer

https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns

0
Simon Hutchison