Issue 1:
Hi React Community, reading the features related to useState and batching, im finding problems where the state is not updated or when i have memoised a method using useCallback

i would like to suggest about add a method to get the state in useState, an example for this approach can be this

const useGetState = (initialValue, withGetCallback = false) => {
const [state, setState] = useState(initialValue);
if (!withGetCallback) {
return [state, setState];
}

const stateRef = useRef(state);
useEffect(() => {
stateRef.current = state;
}, [state]);

const getState = useCallback(() => {
return stateRef.current;
}, [stateRef]);

return [state, setState, getState, stateRef];
};

an implementation could looks like this

const [state, setState, getState] = useGetState(null, true);
// back compatibility
const [state, setState] = useGetState(null); // (exactly same as normal useState)
// complex scenario
const [state, setState, getState, stateRef] = useGetState(null, true);

using useRef we can be able to get the instance of the value in any moment and in any place due that these callbacks never changes during the execution

Issue 2:
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 ;
} ,[])