Matelutex
Matelutex

Reputation: 2220

React - set state doesn't change in callback function

I'm not able to read current state inside refreshWarehouseCallback function. Why? My component:

export function Schedules({ tsmService, push, pubsub }: Props) {
    const [myState, setMyState] = useState<any>(initialState);

 useEffect(() => {
        service
            .getWarehouses()
            .then((warehouses) =>
                getCurrentWarehouseData(warehouses) // inside of this function I can without problems set myState
            )
            .catch(() => catchError());

        const pushToken = push.subscribe('public/ttt/#');
        const pubSubToken = pubsub.subscribe(
            'push:ttt.*',
            refreshWarehouseCallback // HERE IS PROBLEM, when I try to read current state from this function I get old data, state changed in other functions cannot be read in thi function
        );

        return () => {
            pubsub.unsubscribe(pubSubToken);
            push.unsubscribe(pushToken);
        };
    }, []);

...

function refreshWarehouseCallback(eventName: string, content: any) {
      const {warehouseId} = myState; // undefined!!!
      case pushEvents.ramp.updated: {
}
}


return (
        <Views
            warehouses={myState.warehouses}
            allRamps={myState.allRamps}
            currentWarehouse={myState.currentWarehouse}
            pending={myState.pending}
            error={myState.error}
        />

I have to use useRef to store current state additionally to be able to rerender the whole component.

My question is - is there any other solution without useRef? Where is the problem? Calback function doesn't work with useState hook?

Upvotes: 0

Views: 593

Answers (1)

Nick Vu
Nick Vu

Reputation: 15540

Your pub/sub pattern does not inherit React's states. Whenever subscribe is triggered, and your callback function is initialized, that callback will not get any new values from myState.

To be able to use React's states, you can wrap refreshWarehouseCallback into another function like below

//`my state` is passed into the first function (the function wrapper)
//the inner function is your original function
const refreshWarehouseCallback =
  (myState) => (eventName: string, content: any) => {
    const { warehouseId } = myState;
    //your other logic
  };

And then you can add another useEffect to update subscribe after state changes (in this case, myState updates)

 //a new state to store the updated pub/sub after every clean-up
const [pubSubToken, setPubSubToken] = useState();

useEffect(() => {
  //clean up when your state updates
  if (pubSubToken) {
    pubsub.unsubscribe(pubSubToken);
  }
  const updatedPubSubToken = pubsub.subscribe(
    "push:ttt.*",
    refreshWarehouseCallback(myState) //execute the function wrapper to pass `myState` down to your original callback function
  );

  //update new pub/sub token
  setPubSubToken(updatedPubSubToken);

  return () => {
    pubsub.unsubscribe(updatedPubSubToken);
  };

//add `myState` as a dependency
}, [myState]);

//you can combine this with your previous useEffect
useEffect(() => {
  const pushToken = push.subscribe("public/ttt/#");

  return () => {
    pubsub.unsubscribe(pushToken);
  };
}, []);

Upvotes: 1

Related Questions