Home > Blockchain >  useState wouldn't work asynchronously (React)
useState wouldn't work asynchronously (React)

Time:09-28

Building a recording app on React Type Script. I tried to set state with getting stream, and it seems to be successfully gotten on console. But it couldn't be set up on record target stream.

  const [recordTargetStream, setRecordTargetStream] = useState<MediaStream>()


  // click request permissions
  const requestPermissions = useCallback(async() => {
    const stream = await window.navigator.mediaDevices.getUserMedia({audio: true, video: true})
    // stream is successfully gotten
    setRecordTargetStream(stream)
  }, [])


  const startRecording = useCallback(() => {
    console.log('start recording', recordTargetStream)
    // record target stream is undefined    
   ...

  return (
   <>
    <button onClick={() => requestPermissions()}>Request permissions</button>
    <button onClick={() => startRecording()}>Start recording</button>
   </>
  )

CodePudding user response:

You should use the recordTargetStream state as useCallback hook's dependencies.

useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed.

every value referenced inside the callback should also appear in the dependencies array.

index.tsx:

import React, { useCallback, useState } from 'react'

export default function MyComponent() {
  const [recordTargetStream, setRecordTargetStream] = useState<MediaStream>()

  const requestPermissions = useCallback(async() => {
    const stream = await window.navigator.mediaDevices.getUserMedia({audio: true, video: true})
    setRecordTargetStream(stream)
  }, [])

  const startRecording = useCallback(() => {
    console.log('start recording', recordTargetStream)
  }, [recordTargetStream])

  return (
   <>
    <button onClick={() => requestPermissions()}>Request permissions</button>
    <button onClick={() => startRecording()}>Start recording</button>
   </>
  )
}

index.test.tsx:

import {fireEvent, render, screen, act} from '@testing-library/react';
import React from 'react';
import MyComponent from './';

describe('69354798', () => {
  test('should pass', async () => {
    const mockMediaDevices = {
      getUserMedia: jest.fn().mockResolvedValue('test stream')
    }
    Object.defineProperty(window.navigator, 'mediaDevices', {
      value: mockMediaDevices
    })
    render(<MyComponent/>)
    await act(async () => {
      fireEvent.click(screen.getByText(/Request permissions/))
    })
    fireEvent.click(screen.getByText(/Start recording/))
  })
})

test result:

 PASS  stackoverflow/69354798/index.test.tsx
  69354798
    ✓ should pass (54 ms)

  console.log
    start recording test stream

      at stackoverflow/69354798/index.tsx:12:13

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.211 s
  • Related