0

I am trying to use the barcode scanner from react-native-camera. First, off it scans a QR-code and extracts a String, after that it navigates to the next Screen with react-navigation. In the second screen, it makes an API-call.

Now if I go back to the scanner screen, de QR-code will be scanned immediately. That's where I run into an error and the scanner freezes. I usually get this error:

Can't call setState (or forceUpdate) on an unmounted component

I think it's because my componentWillUnmount cleanup doesn't work properly or fast enough, but I already cancel the axios request.

       requestCode = (code) => {
        if (cancel != undefined) {
          cancel();
        }
        axios.get(API_URI + code, {
          cancelToken: new CancelToken(function executor(c) {
            cancel = c;
          })
        }).then(response => {
          console.log(response)
          //checks if code was already called
          this.checkUsed(response.data)
        })
          .catch(error => {
            this.setState({ isValid: false })
          });
        }

    componentWillUnmount() {
        cancel();
      }

Maybe I could mount the camera-scanner a little bit later so it doesn't scan this fast or is it maybe even an error with React Navigation?

Fabio ha
  • 553
  • 1
  • 8
  • 29

1 Answers1

0

You can use a flag to control.

class QR extends Component {
  constructor(props) {
    super(props)

    this.state = {
      scanable: true
    }

    this.cameraAttrs = {
      ref: ref => {
        this.camera = ref
      },
      style: styles.preview,
      type: RNCamera.Constants.Type.back,
      barCodeTypes: [RNCamera.Constants.BarCodeType.qr],
      onBarCodeRead: ({ data }) => {
        this.callback(data)
      }
    }
  }

  componentWillMount() {
    this._mounted = true
  }

  componentWillUnmount() {
    this._mounted = false
  }

  callback(text) {
    if (!this.state.scanable) {
      return
    }

    console.log(text)
    this.setState({ scanable: false })
    setTimeout(() => {
      if (this._mounted) {
        this.setState({ scanable: true })
      }
    }, 1000) // 1s cooldown
  }

  render() {
    return (
      <View style={styles.container}>
        <RNCamera
          {...this.cameraAttrs}
        >
        </RNCamera>
      </View>
    )
  }
}
kigawas
  • 1,153
  • 14
  • 27
  • I think you should use `clearTimeout()` instead of the `_mounted` flag. For example, `this.timeout = setTimeout(...)`; then in `componentWillUnmount` clear the timeout: `if (this.timeout) { clearTimeout(this.timeout) };` – blaze Feb 21 '19 at 21:12