Многочисленные неизменяемые изменения в массивах состояний - PullRequest
3 голосов
/ 10 января 2020

У меня есть следующее currentState. Когда я добавляю получателя, новый получатель должен быть добавлен в receient.allIds и receientProduct с qty = 1 по умолчанию. Я застрял при добавлении к части recipientProduct. Это должно быть сделано в immutable way, поскольку это состояние избыточности.

let currentState = {
  product: {
    allIds: ['1', '5'],
  },
  recipient: {
    allIds: ['1'],
  },
  recipientProduct: [{
        recipientId: '1',
        productId: '1',
        qty: '3'
    }, {
        recipientId: '1',
        productId: '5',
        qty: '3'
    }],
};

Когда я добавляю нового получателя:

addRecipient = (recipientId) => {
    // when recipientId = 2
    // note quantity is always default to 1

    -> append productIds to product.allIds if not exists
    -> loop through all product from product.allIds
         loop through all recipient from recipient.allIds
         if (recipientId === recipientId && productId === productId) {
              remain
          } else {
            // append with default qty 1
        }

}

Это будет newState:

newstate = {
    product: {
        allIds: ['1', '5'],
    },
    recipient: {
        allIds: ['1', '2'],
    },
    recipientProduct: [{
        recipientId: '1',
        productId: '1',
        qty: '3'
    }, {
        recipientId: '1',
        productId: '5',
        qty: '3'
    },{
        recipientId: '2',
        productId: '1',
        qty: '1'
    }, {
        recipientId: '2',
        productId: '5',
        qty: '1'
    }],
}

Ответы [ 3 ]

2 голосов
/ 10 января 2020

Я думаю, что это редуктор, который должен обрабатывать описанную логику c, а не создатель действий (?) addRecipient.

Так что, как я понял, в обычном рабочем процессе Redux вам нужно изменить свойства глобального состояния recipient (должен быть добавлен новый получатель) и recipientProduct (должны добавляться элементы, соответствующие каждому продукту с количеством по умолчанию 1) при отправке действия APPEND_RECIPIENT или такого.

Мое решение для этого было бы следующее:

appReducer = (state=initialState, action) => {
        switch(action.type){
          case 'APPEND_RECIPIENT' : {
            let {payload: recipientId} = action,
                  {recipient:{allIds:recipientIds}, recipientProduct, product:{allIds:productIds}} = state
            if(recipientIds.includes(recipientId)) return state
            recipientIds = [...recipientIds, recipientId]
            recipientProduct = [...recipientProduct,...productIds.map(productId => ({productId, recipientId, qty: '1'}))]
            return {...state, recipient:{allIds:recipientIds}, recipientProduct}
          }
          default: return state
        }
      }

Ниже вы можете найти демонстрационную версию этой концепции:

//dependencies
const { useState } = React,
      { render } = ReactDOM,
      { createStore } = Redux,
      { connect, Provider } = ReactRedux

//initial state, reducer and store
const initialState = {product:{allIds:['1','5']},recipient:{allIds:['1']},recipientProduct:[{recipientId:'1',productId:'1',qty:'3'},{recipientId:'1',productId:'5',qty:'3'}]},
      appReducer = (state=initialState, action) => {
        switch(action.type){
          case 'APPEND_RECIPIENT' : {
            let {payload: recipientId} = action,
                  {recipient:{allIds:recipientIds}, recipientProduct, product:{allIds:productIds}} = state
            if(recipientIds.includes(recipientId)) return state
            recipientIds = [...recipientIds, recipientId]
            recipientProduct = [...recipientProduct,...productIds.map(productId => ({productId, recipientId, qty: '1'}))]
            return {...state, recipient:{allIds:recipientIds}, recipientProduct}
          }
          default: return state
        }
      },
      store = createStore(appReducer)
      
//append recipient form ui component
const AppendRecipient = ({onAppendRecipient, onInput}) => {
  const [inputValue, setInput] = useState()
  return (
    <form onSubmit={e => (e.preventDefault(), onAppendRecipient(inputValue))}>
      <input type="number" onKeyUp={e => setInput(e.target.value)} />
      <input type="submit" name="Append Recipient" />
    </form>
  )
}
//connect onAppendRecipient handler to dispatching 'APPEND_RECIPIENT' action
const mapDispatchToProps = dispatch => ({
        onAppendRecipient: recipientId => dispatch({type:'APPEND_RECIPIENT', payload:recipientId})
      }),
      AppendRecipientContainer = connect(null, mapDispatchToProps)(AppendRecipient)
//mirroring recipientProducts
const RecipientProducts = ({products, productsQty}) => (
  <div>
    {
      products.map(({recipientId,productId,qty},key) => <div {...{key}}>recipient:{recipientId}, product:{productId}, qty: {qty}</div>)
    }
  </div>
)
//connect output element to global recipientProducts
const mapStateToProps = ({recipientProduct}) => ({products:recipientProduct, productsQty:recipientProduct.length}),
      RecipientProductsContainer = connect(mapStateToProps)(RecipientProducts)
      
//render the entire app
render (
  <Provider store={store}>
    <AppendRecipientContainer />
    <RecipientProductsContainer />
  </Provider>,
  document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.1.3/react-redux.min.js"></script><div id="root"></div>
1 голос
/ 10 января 2020

Можете ли вы попробовать эту логи c?

const redux = require("redux");
const createStore = redux.createStore;

const ADD_RECIPIENT = "ADD_RECIPIENT";

const initialState = {
  product: {
    allIds: ["1", "5"]
  },
  recipient: {
    allIds: ["1"]
  },
  recipientProduct: [
    {
      recipientId: "1",
      productId: "1",
      qty: "3"
    },
    {
      recipientId: "1",
      productId: "5",
      qty: "3"
    }
  ]
};

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_RECIPIENT:
      const recipientId = action.recipientId;
      const recipientExists = state.recipient.allIds.includes(recipientId);

      if (!recipientExists) {
        let recipientProducts = [...state.recipientProduct];

        state.product.allIds.forEach(productId => {
          let productIdExists =
            state.recipientProduct.findIndex(r => r.productId === productId) >-1;
          let recipientIdExists =
            state.recipientProduct.findIndex( r => r.recipientId === recipientId) > -1;

          if (!productIdExists || !recipientIdExists) {
            recipientProducts.push({recipientId,productId,qty: "1"});
          }
        });

        return {
          ...state,
          recipient: {
            allIds: [...state.recipient.allIds, recipientId]
          },
          recipientProduct: recipientProducts
        };
      }

      return state;

    default:
      return state;
  }
};

const store = createStore(reducer);

store.subscribe(() => {
  console.log("[NEW STATE]", store.getState());
});

store.dispatch({
  type: ADD_RECIPIENT,
  recipientId: "2"
});
0 голосов
/ 10 января 2020

Вы можете сделать копию своего текущего состояния. Модифицировать свой объект и снова присвоить этому объекту состояние.

addRecipient = (recipientId) => {
    let copy = {...currentState};
    copy.recipient.allIds.push(recipientId);
    copy.product.allIds.map((product)=>{
      let obj = {
        recipientId: recipientId,
        productId: product,
        qty: '1'
      }
      copy.recipientProduct.push(obj);
    });
    //Now `copy` is your new state.Old object will be as it is.
    //You can do anything with new object.
}

Если вы используете redux, тогда вы можете вызвать action или setState и можете изменить состояние , Дайте мне знать, если это вам поможет.

...