Как изменить состояние Redux из строки пути? - PullRequest
1 голос
/ 22 апреля 2020

У меня есть начальное состояние, подобное этому:

const initialState = {
  array: [
    {
      key: "value",
      obj: {
        key1: "value",
        key2: "value",
      },
      array: [
        {
          key: "value",
          obj: {
            key1: "value",
            key2: "value",
          },
        }
      ]
    },
    {
      key: "value",
      obj: {
        key1: "value",
        key2: "value",
      },
    },
    {
      key: "value",
      obj: {
        key1: "value",
        key2: "value",
      },
    },
  ],
  path: "",
  value: ""
};

Редуктор:

export const reducer = (state = initialState, action) => {
  switch (action.type) {
    case "SET_PATH":
      return {
        ...state,
        path: action.path
      };

    case "SET_NEW_VALUE":
      return {
        ...state,
        newValue: action.value
      };

    case "SET_NEW_BUILD":
      //What next?

    default:
      return state
  }
};

Создатели действий:

const setPath = (path) => ({type: "SET_PATH", path});
const setNewValue = (value) => ({type: "SET_NEW_VALUE", value});
const setNewBuild = (path, value) => ({type: "SET_NEW_BUILD", path, value});

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

dispatch(setNewBuild("array[0].obj.key1", "newValue");

Также значение может иметь такую ​​форму "obj: {key1:" newValue ", key2:" newValue "}", следовательно, будет создан новый объект.

Как я могу это сделать?

Ответы [ 3 ]

0 голосов
/ 22 апреля 2020

Чтобы сделать это только с path, есть 2 подхода, помните, что в Redux все должно быть неизменным, поэтому прямые мутации не должны выполняться.

  • Более простой подход: библиотека immer , которая позволяет выполнять мутации, такие как push или с использованием оператора точки, но будучи неизменной снизу.

  • Сложный подход: использовать спреды native JS операторы распространения для объектов и массивов, но вы лучше поймете, как все это работает.

Я оставлю ванильный пример, но если вы предпочитаете идти с чем-то прямым, вы можно использовать библиотеку immer.

function reducer(state = initialState, action) {
  switch (action.type) {
    case 'setNewData': {
      const { array } = state;
      const { path, value } = action.payload;

      const index = path.match(/[\d]+/g);
      const objectPath = path.split('.').slice(1);

      const [elementToReplace] = array.slice(index, index + 1);
      _.set(elementToReplace, objectPath, value); // using lodash set helper here

      const newArray = [...array.slice(0, index), elementToReplace, ...array.slice()];

      return {
        ...state,
        array: newArray,
      };
    }

    default: {
      return state;
    }
  }
}
0 голосов
/ 22 апреля 2020

Вот пример использования помощника set :

const REMOVE = () => REMOVE;
//helper to get state values
const get = (object, path, defaultValue) => {
  const recur = (current, path, defaultValue) => {
    if (current === undefined) {
      return defaultValue;
    }
    if (path.length === 0) {
      return current;
    }
    return recur(
      current[path[0]],
      path.slice(1),
      defaultValue
    );
  };
  return recur(object, path, defaultValue);
};
//helper to set state values
const set = (object, path, callback) => {
  const setKey = (current, key, value) => {
    if (Array.isArray(current)) {
      return value === REMOVE
        ? current.filter((_, i) => key !== i)
        : current.map((c, i) => (i === key ? value : c));
    }
    return value === REMOVE
      ? Object.entries(current).reduce((result, [k, v]) => {
          if (k !== key) {
            result[k] = v;
          }
          return result;
        }, {})
      : { ...current, [key]: value };
  };
  const recur = (current, path, newValue) => {
    if (path.length === 1) {
      return setKey(current, path[0], newValue);
    }
    return setKey(
      current,
      path[0],
      recur(current[path[0]], path.slice(1), newValue)
    );
  };
  const oldValue = get(object, path);
  const newValue = callback(oldValue);
  if (oldValue === newValue) {
    return object;
  }
  return recur(object, path, newValue);
};
const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore } = Redux;

//action
const setNewBuild = (path, value) => ({
  type: 'SET_NEW_BUILD',
  path,
  value,
});
const initialState = {
  array: [
    {
      key: 'value',
      obj: {
        key1: 'value',
        key2: 'value',
      },
    },
  ],
  path: '',
  value: '',
};
const reducer = (state = initialState, action) => {
  const { type } = action;
  if (type === 'SET_NEW_BUILD') {
    const { path, value } = action;
    return set(state, path, () => value);
  }
  return state;
};
const store = createStore(
  reducer,
  initialState,
  window.__REDUX_DEVTOOLS_EXTENSION__ &&
    window.__REDUX_DEVTOOLS_EXTENSION__()
);
const App = () => {
  const state = useSelector((x) => x);
  const dispatch = useDispatch();
  return (
    
      
      
      {JSON.stringify(state, undefined, 2)}
); }; ReactDOM.render ( , document.getElementById ('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/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.2.0/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>


<div id="root"></div>

Важные биты:

<button
  onClick={() =>
    dispatch(
      // path is an array
      setNewBuild(['array', 0, 'obj'], {
        key1: 'change both key1',
        key2: 'change both key2',
      })
    )
  }
>
  change array 0 obj
</button>

А в редукторе:

const { type } = action;
if (type === 'SET_NEW_BUILD') {
  const { path, value } = action;
  return set(state, path, () => value);
}
0 голосов
/ 22 апреля 2020

getState дает вам текущее состояние во время отправки действия, также обратите внимание, что вы отправляете действия как функции более высокого порядка (UI-dispatch(action(payload => dispatch => dispatch({type, payload}))


 //assuming { value } comes from UI and { path } refer to current stored data at state
 const action = value => (dispatch, getState) => 
        dispatch({type: "netNewData", value, path: getState().array[0].obj.key1}) ;


 const reducer = ( state = initialState, action) => {
  switch(action.type){
   case "setNewData":
      const { value, path } = action;
      return {...state, value, path}
   default: return state
  }
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...