0

I'm trying to clear fields from a form (clearing state from the store) when the user changes their country so I was wondering if it was possible to dispatch two actions under one event... -- tho my action also doesn't clear the fields so not sure where I'm going wrong

in index.jsx

export default function Form() {
  const {
    apartmentNumber,
    birthDay,
    birthMonth,
    birthYear,
    buildingNumber,
    countryCode
  } = state;

  const [formData, setFormData] = useState({
    apartmentNumber,
    birthDay,
    birthMonth,
    birthYear,
    buildingNumber
  });

const onInputChange = (attribute, value) => {
    setFormData({
      ...formData,
      [attribute] : value
    });
  };

const onCountryChange = (value) => {
    dispatch(updateCountry(value));
    dispatch(clearForm(formData));
  };

in reducer.js --

export const initialState = {
  apartmentNumber           : '',
  birthDay                  : '',
  birthMonth                : '',
  birthYear                 : '',
  buildingNumber            : ''
};

export default (state, action) => {
  const { payload, type } = action;
  switch (type) {
    case UPDATE_COUNTRY:
      return {
        ...state,
        countryCode : payload
      };
    case UPDATE_FIELDS: {
      return {
        apartmentNumber : initialState.apartmentNumber,
        birthDay        : initialState.birthDay,
        birthMonth      : initialState.birthMonth,
        birthYear       : initialState.birthYear,
        buildingNumber  : initialState.buildingNumber
      };
    }
    default:
      return state;
  }
};
  • 1
    Look into `redux-thunk`. – lux Aug 17 '21 at 20:14
  • Why are you using reducers and state like they are interchangeable? If you are rendering your components with usestate, I don't think your components will rerender when change the reducer state. For this case I would suggest keep one of them and use that or usestate inside isolated components but pass the props from the reducer. – Ricardo Silva Aug 17 '21 at 20:20
  • Are you using useReducer in your Rect component? You can build the reducer to handle arrays of actions, applying each of them to the state before triggering a state propagation through the tree. – Jacob Penney Aug 17 '21 at 20:35

3 Answers3

1

You can reset the values be passing in initialState. Ideally, you should have an action for when UPDATE_COUNTRY is successful. Then you can reset to initialState once the country has been successfully updated.

    case UPDATE_COUNTRY_SUCCESS:
        return initialState;

or if you don't want to add a success action, you can just do

    case UPDATE_COUNTRY:
        return {
          ...initialState,
          countryCode: payload
        };

0

As for dispatching multiple actions. You can use redux or if your reducer does not do a side effect you can change your reducer to handle these at one go.

See: Sending multiple actions with useReducers dispatch function?

If your output is not side-effecty you can do similar to: https://codezup.com/how-to-combine-multiple-reducers-in-react-hooks-usereducer/ I don't think you need to use context api for that just pass the reducer and state to the components you want to call dispatch from.

If they have side effects you can achieve chain the effects by setting a reducer which returns the next effect to be ran from useEffect this is useful if you need the ui to change in each disptach. For multiple effects you can combine them and make then all run in one useEffect.

Other than that libs like redux handle these basically out of the box. But I have never used redux.

Ricardo Silva
  • 1,221
  • 9
  • 19
0

You can do this using useReducer React hook. Take note of the createReducer function and how it can be composed to handle arrays of actions.

const createReducer = (actions, state) {
    return (state, action) => {
        if(Array.isArray(action)) {
            actions.forEach(action => {
                state = actions[action[i].type](state, action[i])
            })
            return state
        } else if(actions[action.type]) {
            return actions[action.type](state, action)
        } else {
            return state
        }
    }
}

const actions = {
  INCREMENT: (state, action) => {
    state.counter++
    return state
  }
}

const initState = () => ({
  counter: 0
})

const reducer = createReducer(actions)

const App = () => {
  const [state, setState] = React.useReducer(reducer, initState())
  return <div>Count: {state.count} 
    <button onClick={e => setState([
      {type: 'INCREMENT'},
      {type: 'INCREMENT'}
    ])}>+</button>
  </div>
}

I suspect your issue is that state is propagating through the DOM tree with every actions dispatched, which can lead to broken or weird DOM states. With this architecture, you apply each of the actions in the array before the state is returned, meaning propagation only occurs after all actions have been applied to the state.

Jacob Penney
  • 725
  • 4
  • 9