Как безопасно обновлять свое состояние, когда мне нужно просматривать и искать / удалять элементы в моем состоянии - PullRequest
0 голосов
/ 05 сентября 2018

Мне нужно изменить свое состояние, и я не уверен, как это сделать правильно.

Свойство моего аккаунта в моем состоянии выглядит примерно так:

{     
 "account":{  
    "id":7,    
    "categories":[  
       {  
          "id":7,          
          "products":[  
             {                  
                "productId":54                
             }
          ]
       },
       {  
          "id":9,          
          "products":[  
             {                  
                "productId":89                
             }
          ]
       }
    ]
 }
}

Мое действие отправляет следующее:

dispatch({
  type: Constants.MOVE_PRODUCT,
  productId: 54,
  sourceCategoryId: 7,
  targetCategoryId: 9
});

Теперь мой каркас редуктора:

const initialState = {
  account: null,
};


const accounts = (state = initialState, action) => {
  switch (action.type) {
    case Constants.MOVE_PRODUCT:
      /*
      action.productId
      action.sourceCategoryId
      action.targetCategoryId
      */

      const sourceCategoryIndex = state.account.categories.findIndex((category) => { return category.id === action.sourceCategoryId; });
      const sourceCategory = state.account.categories[sourceCategoryIndex];

      const targetCategoryIndex = state.account.categories.findIndex((category) => { return category.id === action.targetCategoryId; });
      const targetCategory = state.account.categories[targetCategoryIndex];

    // ??

      return {...state};
  }
}

export default accounts;

Я запутался, если я обновляю состояние непосредственно внутри блока коммутатора, это неправильно?

Это должно быть однострочное обновление, которое выполняет мутацию на месте, или пока я делаю это в блоке переключателей, все в порядке?

Обновление

Из действия мне нужно удалить productId из sourceCategoryId и добавить его в targetCategoryId внутри объекта состояния учетной записи.

Ответы [ 4 ]

0 голосов
/ 05 сентября 2018

Вот ужасное решение:)

const accounts = (state = initialState, action) => {
  switch (action.type) {
    case Constants.MOVE_PRODUCT:
      const sourceCategoryIndex = state.account.categories.findIndex(
        el => el.id === action.sourceCategoryId
      );

      const targetCategoryIndex = state.account.categories.findIndex(
        el => el.id === action.targetCategoryId
      );

      const sourceCategory = state.account.categories.find(
        el => el.id === action.sourceCategoryId
      );

      const targetCategory = state.account.categories.find(
        el => el.id === action.targetCategoryId
      );

      const itemToMove = sourceCategory.products.find(
        el => el.productId === action.productId
      );

      const newSourceCategory = {
        ...sourceCategory,
        products: sourceCategory.products.filter(
          el => el.productId !== action.productId
        )
      };

      const newTargetCategory = {
        ...targetCategory,
        products: [...targetCategory.products, itemToMove]
      };

      const newCategories = Object.assign([], state.account.categories, {
        [sourceCategoryIndex]: newSourceCategory,
        [targetCategoryIndex]: newTargetCategory
      });

      return { ...state, account: { ...state.account, categories: newCategories } };
  }
};

Фу :) Как ученик, это очень хорошо для меня :) Но мне нравится подход @Daniel Lizik, использующий reduce.

Вот рабочий пример:

const action = {
  productId: 54,
  sourceCategoryId: 7,
  targetCategoryId: 9,
}

const state = {     
 "account":{  
    "id":7,    
    "categories":[  
       {  
          "id":7,          
          "products":[  
             {                  
                "productId":54,             
             },
             {                  
                "productId":67,             
             },
          ]
       },
       {  
          "id":9,          
          "products":[  
             {                  
                "productId":89,               
             }
          ]
       }
    ]
 }
};

const sourceCategoryIndex = state.account.categories.findIndex( el => el.id === action.sourceCategoryId );
const targetCategoryIndex = state.account.categories.findIndex( el => el.id === action.targetCategoryId );

const sourceCategory = state.account.categories.find( el => el.id === action.sourceCategoryId );
const targetCategory = state.account.categories.find( el => el.id === action.targetCategoryId );

const itemToMove = sourceCategory.products.find( el => el.productId === action.productId );

const newSourceCategory = {...sourceCategory, products: sourceCategory.products.filter( el => el.productId !== action.productId ) };

const newTargetCategory = { ...targetCategory, products: [ ...targetCategory.products, itemToMove ] };

const newCategories = Object.assign([], state.account.categories, {             [sourceCategoryIndex]: newSourceCategory,
   [targetCategoryIndex]: newTargetCategory }
);

const newState = { ...state, account: { ...state.account, categories: newCategories } };

console.log( newState );
0 голосов
/ 05 сентября 2018

Вы можете использовать следующий подход:

const accounts = (state = initialState, action) => {
  switch (action.type) {
    case Constants.MOVE_PRODUCT:

      // Extract action parameters
      const { productId, sourceCategoryId, targetCategoryId } = action

      // Manually "deep clone" account state
      const account = {
        id : state.account.id,
        categories : state.account.categories.map(category => ({          
          id : category.id,
          products : category.products.map(product => ({ productId : product.productId })
        }))
      }

      // Extract source and target categories
      const sourceCategory = account.categories.find(category => category.id === sourceCategoryId);
      const targetCategory = account.categories.find(category => category.id === targetCategoryId);

      if(sourceCategory && targetCategory) {

        // Find product index
        const index = sourceCategory.products.findIndex(product => (product.productId === action.productId))
        if(index !== -1) {

          const product = sourceCategory.products[index]

          // Remove product from source category
          sourceCategory.products.splice(index, 1) 

          // Add product to target category
          targetCategory.products.splice(index, 0, product) 
        }
      }

      return { account };
  }
}
0 голосов
/ 05 сентября 2018

Да, вы не должны делать state.foo = 'bar' в своем редукторе. Из избыточных документов :

Мы не мутируем государство. Мы создаем копию с Object.assign(). Object.assign(state, { visibilityFilter: action.filter }) также неверно: он будет мутировать первый аргумент. Вы должны предоставить пустой объект в качестве первого параметра. Вы также можете включить предложение оператора распространения объекта для записи { ...state, ...newState }.

Так что ваш редуктор может выглядеть как

function accountsReducer (state = initialState, { sourceCategoryId, productId }) {
  const targetProduct = state.categories
    .find(({ id }) => id === sourceCategoryId)
    .products
    .find(({ id }) => id === productId);

  switch (action.type) {
    case Constants.MOVE_PRODUCT:
      return {
        ...state,
        categories: state.categories.reduce((acc, cat) => {
          return cat.id !== sourceCategoryId
            ? {
                ...acc,
                cat: { ...cat, products: cat.products.filter(({ id }) => id !== productId) }
              }
            : {
                ...acc,
                cat: { ...cat, products: [...cat.products, targetProduct] }
              }
        }, {});
      };
  }
}

Но это боль ... вы должны попытаться нормализовать ваши данные в плоский массив.

0 голосов
/ 05 сентября 2018
// first, let's clean up the action a bit
// type and "payload". I like the data wrapped up in a bundle with a nice
// bow on it. ;) If you don't like this, just adjust the code below. 
dispatch({
  type: Constants.MOVE_PRODUCT,
  payload: { 
      product: { productId: 54 }
      sourceCategoryId: 7,
      targetCategoryId: 9
  }
});

// destructure to get our id and categories from state
const { id, categories } = state

// map the old categories to a new array
const adjustedCategories = categories.map(cat => {
    // destructure from our payload
    const { product, sourceCategoryId, targetCategoryId } = action.payload

    // if the category is the "moving from" category, filter out the product
    if (cat.id === sourceCategoryId) {
        return { id: cat.id, products: [...cat.products.filter(p => p.productId !== product.productId)
    }

    // if the category is our "moving to" category, use the spread operator and add the product to the new array
    if (cat.id === targetCategoryId) {
        return { id: cat.id, products: [...cat.products, product] }
    }
)

// construct our new state
return { id, categories: adjustedCategories }

Это решение поддерживает чистоту функции и должно дать вам то, что вы хотите. Это не проверено, поэтому не может быть идеальным.

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