1

how can i use useReducer react js with firestore? it always return undifined

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;
KH K
  • 13
  • 2

1 Answers1

0

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;
  }
}
Maaz Bin Khawar
  • 435
  • 3
  • 13
  • Thanks for your quick and kind reply. I am a strength in the midst of so much confusion. But I tried to fix the code as you said, but I get the following error: TypeError: action.order is not iterable src/Counter.js/18:6 – KH K Jul 26 '21 at 09:08
  • From you code snippet it is evident that when you dispatch "ORDERS" action, you're passing providing an array in the order key. So, the error is probably coming from another action. Can you look for other action(s) of type = "ORDERS" in your code. Temporarily, you can also use `return {...state, orders: [...state.orders, ...(action.order || [])] }` in your reducer to cater for nullish order values. – Maaz Bin Khawar Jul 26 '21 at 09:24
  • Sadly we don't use case "ORDERS": anywhere else. The code above is all my code. Even if I fix it as you said, I get the error TypeError: action.order is not iterable. I really don't know what the problem is. Can't useEffect and dispatch be used together? Do you have a project where you have successfully implemented useEffect and dispatch? Or how do you design your first data fetching? – KH K Jul 26 '21 at 09:34
  • There is not such limitation of using useEffect and dispatch together. The error message only suggests that the action.order value is not iterable, which it apparently looks. To debug this, can you add a log inside your reducer temporarily and paste it's result here to see what does the action actually contain? Something like: `case "ORDERS": console.log({action}); return {...state, orders: [...state.orders, ...action.order] };` – Maaz Bin Khawar Jul 26 '21 at 09:42
  • {action: {…}} action: order: data: {address: "seoul", paymentMethod: "transfer", createdAt: t, customer: "interasiadev@gmail.com", orderNumber: "1000", …} id: "1000" [[Prototype]]: Object type: "ORDERS" [[Prototype]]: Object – KH K Jul 26 '21 at 11:52
  • It returns an object. What is the problem? The object returned by this is the one I wanted. – KH K Jul 26 '21 at 11:54
  • case "ORDERS": return { ...state, orders: [...state.orders, { ...action.order }] }; – KH K Jul 26 '21 at 11:57
  • Editing this solved the problem. Thanks to your time and help, I was able to solve it. I am so grateful. God bless you – KH K Jul 26 '21 at 11:58
  • If you find my answer as a solution to your problem, please mark it as accepted and upvote if you like. – Maaz Bin Khawar Jul 26 '21 at 12:08