Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature request: getState, example, const [state, setState, getState] = useState(null); #21931

Closed
shimondoodkin opened this issue Jul 21, 2021 · 9 comments
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug

Comments

@shimondoodkin
Copy link

shimondoodkin commented Jul 21, 2021

feature request: getState, example, const [state, setState, getState] = useState(null);

it is ok to use the simple variable in a dependent uses effect, but not ok in not a dependant use effect.

the state is a simple variable. which is closure passed into functions, so it is not updated.
and setState with a function argument is asynchronously dispatched.
I suggest adding a synchronous function getState.

it will be useful in simple js functions that are defined in useEffect for integration with not react js

  const [audioTrack, setAudioTrack] = useState(null);
  const [savedSelectedAudioTrack, setSavedSelectedAudioTrack] = useState(null);
  const [audioContext, setAudioContext] = useState(null);

// use useEffect to run once
useEffect( () =>{
const handleParticipant = (participant) => {

          const availableTracks = Array.from(
            participant.tracks.values()
          ).filter(
            (publication) => publication.isSubscribed,
          ).map(
            (publication) => publication.track,
          );

          const audioTracks = availableTracks.filter(
            (track) => track.kind === 'audio',
          );
          
          const selectedAudioTrack =
            audioTracks.length === 0 ? null : audioTracks[0];

          if (selectedAudioTrack !== savedSelectedAudioTrack) { // here savedSelectedAudioTrack will not work
            setSavedSelectedAudioTrack(selectedAudioTrack);
            if (audioContext)
              setAudioTrack(
                // eslint-disable-next-line prettier/prettier
                audioContext.MediaStreamAudioSourceNode(selectedAudioTrack)
              );
            else setTimeout(() => handleParticipant(participant), 100);

            // setSelectedAudioTrack(selectedAudioTrack1);
            window.hasAudioTrack = true;
          } else {
            window.hasAudioTrack = false;
          }


          console.log(
            'audioTracks.length:',
            audioTracks.length
          );
  };


  // create audiocontext and 3rd parity library and add event handler to 3rd parity library
 setAudioContext(createdAudioContext);
 3rdParityLibrary.handleEvent=handleParticipant ;
} ,[])

but the working implementation is like

  const [audioTrack, setAudioTrack] = useState(null);
  const [savedSelectedAudioTrack, setSavedSelectedAudioTrack] = useState(null);
  const [audioContext, setAudioContext] = useState(null);

// use useEffect to run once
useEffect( () =>{
const handleParticipant = (participant) => {

      setSavedSelectedAudioTrack((savedSelectedAudioTrack) => {
        setAudioContext((audioContext) => {

          const availableTracks = Array.from(
            participant.tracks.values()
          ).filter(
            (publication) => publication.isSubscribed,
          ).map(
            (publication) => publication.track,
          );

          const audioTracks = availableTracks.filter(
            (track) => track.kind === 'audio',
          );
          
          const selectedAudioTrack =
            audioTracks.length === 0 ? null : audioTracks[0];

          if (selectedAudioTrack !== savedSelectedAudioTrack) { // here savedSelectedAudioTrack will not work
            setSavedSelectedAudioTrack(selectedAudioTrack);
            if (audioContext) // and audio context also will not work
              setAudioTrack(
                // eslint-disable-next-line prettier/prettier
                audioContext.MediaStreamAudioSourceNode(selectedAudioTrack)
              );
            else setTimeout(() => handleParticipant(participant), 100);

            // setSelectedAudioTrack(selectedAudioTrack1);
            window.hasAudioTrack = true;
          } else {
            window.hasAudioTrack = false;
          }


          console.log(
            'audioTracks.length:',
            audioTracks.length
          );
          
          
          return audioContext;
        });
        return savedSelectedAudioTrack;
      });
  };


  // create audiocontext and 3rd parity library and add event handler to 3rd parity library
 setAudioContext(createdAudioContext);
 3rdParityLibrary.handleEvent=handleParticipant ;
} ,[])

i wish there was a synchronous getState.

  const [audioTrack, setAudioTrack] = useState(null);
  const [savedSelectedAudioTrack, setSavedSelectedAudioTrack, getSavedSelectedAudioTrack] = useState(null);
  const [audioContext, setAudioContext, getAudioContext] = useState(null);

// use useEffect to run once
useEffect( () =>{
const handleParticipant = (participant) => {

          const availableTracks = Array.from(
            participant.tracks.values()
          ).filter(
            (publication) => publication.isSubscribed,
          ).map(
            (publication) => publication.track,
          );

          const audioTracks = availableTracks.filter(
            (track) => track.kind === 'audio',
          );
          
          const selectedAudioTrack =
            audioTracks.length === 0 ? null : audioTracks[0];

          if (selectedAudioTrack !== getSavedSelectedAudioTrack()) { // here savedSelectedAudioTrack will not work
            setSavedSelectedAudioTrack(selectedAudioTrack);
            if (getAudioContext())
              setAudioTrack(
                getAudioContext().MediaStreamAudioSourceNode(selectedAudioTrack)
              );
            else setTimeout(() => handleParticipant(participant), 100);

            // setSelectedAudioTrack(selectedAudioTrack1);
            window.hasAudioTrack = true;
          } else {
            window.hasAudioTrack = false;
          }


          console.log(
            'audioTracks.length:',
            audioTracks.length
          );
  };


  // create audiocontext and 3rd parity library and add event handler to 3rd parity library
 setAudioContext(createdAudioContext);
 3rdParityLibrary.handleEvent=handleParticipant ;
} ,[])
@shimondoodkin shimondoodkin added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Jul 21, 2021
@Ayc0
Copy link

Ayc0 commented Jul 22, 2021

I agree that this would be nice indeed.

At the moment, when I need to be able to retrieve dynamically the current state, I'm doing things like:

const [state, setState] = useState();
const stateRef = useRef();
stateRef.current = state;

...

// in a callback somewhere:
const currentState = stateRef.current

which isn't safe for concurrent mode, but setting the ref in useEffect doesn't work if the callback is called synchronously.


And sometimes, I'm also doing:

const [state, setState] = useState();

...

// in a callback somewhere:
setState(currentState => {
 // here I have the current value of the state
 return setState;
});

but this is kinda weird and breaks the flow of the code (and can trigger re-renders the 1st time it gets called)

@Ayc0
Copy link

Ayc0 commented Aug 1, 2021

@shimondoodkin shouldn't this issue be moved to https://github.com/reactjs/rfcs?

@shimondoodkin
Copy link
Author

@Ayc0 I don't know to answer, nither I think I am suting to implement this.

@Ayc0
Copy link

Ayc0 commented Aug 1, 2021

Btw, I saw this twitter thread explaining the reasons behind why getState isn't returned by useState / useReducer:
https://twitter.com/AdamRackis/status/1096863190049345536

@shimondoodkin
Copy link
Author

shimondoodkin commented Aug 19, 2021

about the Twitter thread,

there was no significant reasoning against it. just associated thoughts, summary, and my arguments:

there were reasons like 'use reducers instead'. I think they are not relevant because it is possible to put the value somewhere else anyway, the issue is more about convenience, expressiveness, flexibility.

it said there, there is no promise of in-order execution for a getState method, however, it is not expected from it.

@crodriguez-plitzi
Copy link

@shimondoodkin @Ayc0 check my proposal that was closed by @bvaughn because was duplicated the suggestion about getState work in all scenarios including methods memoised and with useCallback

@bvaughn
Copy link
Contributor

bvaughn commented Sep 29, 2021

@Ayc0 was right. Changes like this should go through our RFC process:
https://github.com/reactjs/rfcs#react-rfcs

The RFC process is a great opportunity to get more eyeballs on your proposal before it becomes a part of a released version of React. Quite often, even proposals that seem "obvious" can be significantly improved once a wider group of interested people have a chance to weigh in.

The RFC process can also be helpful to encourage discussions about a proposed feature as it is being designed, and incorporate important constraints into the design while it's easier to change, before the design has been fully implemented.

@crodriguez-plitzi
Copy link

@bvaughn quite similar to this reactjs/rfcs#201 just that he called useStateRef

@bvaughn
Copy link
Contributor

bvaughn commented Sep 30, 2021

Agreed. It looks similar. Let's actually close this issue and roll it into that RFC. (Sorry to keep moving things around. This sort of proposal really should have been an RFC to begin with.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug
Projects
None yet
Development

No branches or pull requests

4 participants