reactjsfirebasegoogle-cloud-firestorereact-hooks

How can I use useReducer react js with firestore? It always returns undefined


How can I use useReducer react js with firestore? It always returns "undefined".

I'm trying to use reducer with firebase, but if I use dispatch in useEffect, I can't get the array.

The userType works fine. What did I do wrong?

When the app.js page is first rendered, I want to load the logged-in user data, user data

And order information product information in firestore, put it in the state, and manage it.

If there is a better way, please recommend it.

const iState = {
  orders: [],
  userType: "before",
};
function reducer(state, action) {
  switch (action.type) {
    case "ORDERS":
      return { orders: [...state.orders, [action.order]] };
    case "CHANGE":
      return { userType: action.userType };
    default:
      return state;
  }
}
function Counter() {
  const [user, loading] = useAuthState(auth);
  const [state, dispatch] = useReducer(reducer, iState);
  const { orders, userType } = state;
  useEffect(() => {
    db.collection("accounts")
      .doc(user?.email)
      .get()
      .then(doc => dispatch({ type: "CHANGE", userType: doc?.data()?.type }));
    db.collection("orders")
      .doc("b2b")
      .collection("b2borders")
      .onSnapshot(snapshot =>
        dispatch({
          type: "ORDERS",
          order: snapshot.docs.map(doc => ({ id: doc.id, data: doc.data() })),
        })
      );
  }, [user, dispatch]);
  if (loading || userType === "before") {
    return (
      <div className="grid place-items-center h-screen w-full">
        <div className="text-center pb-24 flex flex-col justify-center items-center">
          <Spinner name="ball-spin-fade-loader" color="gray" fadeIn="none" />
        </div>
      </div>
    );
  }
  if (user && userType === "admin") {
    return (
      <div className="grid place-items-center h-screen w-full">
        {console.log(orders)}
      </div>
    );
  }
  return (
    <>
      <div className="grid place-items-center h-screen w-full">
        <div className="text-center pb-24 flex flex-col justify-center items-center">
          <Spinner name="ball-spin-fade-loader" color="gray" fadeIn="none" />
        </div>
      </div>
    </>
  );
}
export default Counter;

Solution

  • You've not written your reducer correctly. A reducer is a pure function which takes the current state and dispatched action as inputs and returns the new state. So, whenever you return the new state object from the reducer, it replaces your current state object completely instead of merging your new state with the current.

    So, you have to change your reducer to something like this:

    function reducer(state, action) {
      switch (action.type) {
        case "ORDERS":
          return {...state, orders: [...state.orders, {...action.order}] };
        case "CHANGE":
          return {...state, userType: action.userType };
        default:
          return state;
      }
    }