Skip to content
This repository has been archived by the owner on Jan 1, 2025. It is now read-only.

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Question: Conditional setRecoilState #977

Closed
Nasicus opened this issue Apr 20, 2021 · 2 comments
Closed

Question: Conditional setRecoilState #977

Nasicus opened this issue Apr 20, 2021 · 2 comments

Comments

@Nasicus
Copy link

Nasicus commented Apr 20, 2021

So this more of a "how should this be done?" question and may be stupid, but is something which I ran into quite a few times already.

Let's imagine I have this custom hook:

export function useHyperDyperSubscription() {
  const client = useSuperClient();
   const setMyState = useSetRecoilState(myState);

  useEffect(() => {
    const sub = client.subscribe(data => {
     // in here I want to call setMyState conditionally
     // basically I only want to set the state if state.specialValue changed
     // now the only possiblity I have is to always set the state:
     setMyState(s => ({...s, specialValue: data});
    });

    return () => sub.unsubscribe();
  }, []);
}

I can think of a few fixes for this, but I like none of them.

  1. Via a dependency:
export function useHyperDyperSubscription() {
  const client = useSuperClient();
   const [myState, setMyState] = useRecoilState(myState);

  useEffect(() => {
    const sub = client.subscribe(data => {
     if (data !== myState?.specialValue) return;

     setMyState(s => ({...s, specialValue: data});
    });

    return () => sub.unsubscribe();
  // this now results in having to unsubscribe needlessly "all" the time, maybe this is "heavy" or "expensiv" or happens quite a lot
  }, [myState?.specialValue]);
}
  1. Via useRef (seems like the most ugly hack):
export function useHyperDyperSubscription() {
  const client = useSuperClient();
   const [myState, setMyState] = useRecoilState(myState);
   const myStateRef = useRef(myState);
  // update the ref whenever the state changed
   myStateRef.current = myState;

  useEffect(() => {
    const sub = client.subscribe(data => {
     if (data !== myStateRef.current.mySpecialValue) return;

     setMyState(s => ({...s, specialValue: data});
    });

    return () => sub.unsubscribe();
  }, []);
}

As I said I don't like any of them.
In my opinion the cooles would be if you could do something like this...

export function useHyperDyperSubscription() {
  const client = useSuperClient();
   const setMyState = useSetRecoilState(myState);

  useEffect(() => {
    const sub = client.subscribe(data => {

     setMyState(s => {
        if (s.specialValue === data) {
           return undefined;  //do NOT actually update the state
        }
        
        return {...s, specialValue: data};
      });
    });

    return () => sub.unsubscribe();
  }, []);
}

Maybe this is even already somehow possible?

@BenjaBobs
Copy link

BenjaBobs commented Apr 20, 2021

This seems like it's more of a discussion topic than an issue.

To answer your question though, I think you can achieve what you want through Atom Effects.
You could make an effect that you apply to myState (assuming it's an atom), that does the check for .specialValue.
But to be honest, I think your proposed solution 1 is the most clear in it's intent.
As you mention, it comes with a performance cost though, unless you expose a method to get the value from the lastest snapshot as seen in one of the many "how-to-access-recoil-state-outside-of-react" threads.

@fc1943s
Copy link

fc1943s commented Apr 20, 2021

You can try a recoil callback. Not sure if the code below is working though.


export function useHyperDyperSubscription() {
  const client = useSuperClient();
   
  const update = useRecoilCallback(({snapshot, set}) => async (data) => {
    const myStateValue = await snapshot.getPromise(myState);
	if (myStateValue.specialValue !== data) {
	   set(myState, {...myStateValue, specialValue: data});
	}
  }, []);

  useEffect(() => {
    const sub = client.subscribe(data => {
	 update(data).then();
    });

    return () => sub.unsubscribe();
  }, []);
}

@facebookexperimental facebookexperimental locked and limited conversation to collaborators Apr 20, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants