zoukankan      html  css  js  c++  java
  • [React] Safely setState on a Mounted React Component through the useEffect Hook

    In the class version of this component, we had a method called safeSetState which would check whether the component was still mounted before trying to call setState. This is because our graphql client library is unable to cancel in-flight requests. Let's make that same kind of thing work by tracking the mounted state of our component using the useRef and useEffect hooks.

    We want a "lock", which should run once when component inited, after component unmounted, it should be reseted.

    We can use 'useRef' to build a container to hold our lock:

      const mountedRef = useRef(false);

    Then we can use useEffect:

      useEffect(() => {
        mountedRef.current = true
        return () => (mountedRef.current = false)
      }, [])

    The reason to use '[]' as second arguement, is because we don't want useEffect be triggered second times, we only want to run once, therefore, we use empty array, it won't trigger scecond time.

    Then we can create a safe setSetate function:

      const [state, setState] = useReducer(
        (state, newState) => ({...state, ...newState}),
        {
          loaded: false,
          fetching: false,
          data: null,
          error: null,
        },
      )
    
     const setSafeState = (...args) => mountedRef.current && setState(...args);

    ----

    Full code:

    import {useContext, useReducer, useEffect, useRef} from 'react'
    import PropTypes from 'prop-types'
    import isEqual from 'lodash/isEqual'
    import * as GitHub from '../../../github-client'
    
    function useSetState(initialState) {
      return useReducer(
        (state, newState) => ({...state, ...newState}),
        initialState,
      )
    }
    
    function useSafeSetState(initialState) {
      const [state, setState] = useSetState(initialState)
    
      const mountedRef = useRef(false)
      useEffect(() => {
        mountedRef.current = true
        return () => (mountedRef.current = false)
      }, [])
      const safeSetState = (...args) => mountedRef.current && setState(...args)
    
      return [state, safeSetState]
    }
    
    function Query({query, variables, normalize = data => data, children}) {
      const client = useContext(GitHub.Context)
      const [state, setState] = useSafeSetState({
        loaded: false,
        fetching: false,
        data: null,
        error: null,
      })
    
      useEffect(() => {
        if (isEqual(previousInputs.current, [query, variables])) {
          return
        }
        setState({fetching: true})
        client
          .request(query, variables)
          .then(res =>
            setState({
              data: normalize(res),
              error: null,
              loaded: true,
              fetching: false,
            }),
          )
          .catch(error =>
            setState({
              error,
              data: null,
              loaded: false,
              fetching: false,
            }),
          )
      })
    
      const previousInputs = useRef()
      useEffect(() => {
        previousInputs.current = [query, variables]
      })
    
      return children(state)
    }
    
    Query.propTypes = {
      query: PropTypes.string.isRequired,
      variables: PropTypes.object,
      children: PropTypes.func.isRequired,
      normalize: PropTypes.func,
    }
    
    export default Query
  • 相关阅读:
    递推最小二乘辨识平面双机械臂Matlab代码
    Matlab Robitic Toolbox学习笔记Day2
    DSP28335与CH340使用心得
    DSP 28335 GPIO输入引脚信号跳变
    DSP 28335 RS485 SCI串口通讯 出错无法进入中断
    DSP 28335 烧写FLASH程序 可以在线仿真 无法离线运行
    CRC16-Modbus 校验 C语言
    C语言 负数转十六进制 电机转速 CANopen命令转换
    CAN转USB 调试助手使用说明
    串的模式匹配 BF算法&KMP算法
  • 原文地址:https://www.cnblogs.com/Answer1215/p/10410150.html
Copyright © 2011-2022 走看看