xgqfrms™, xgqfrms® : xgqfrms's offical website of cnblogs! xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!

React useEffect in depth

React useEffect in depth

useEffect

class DogInfo extends React.Component {
  controller = null
  state = {dog: null}
  // we'll ignore error/loading states for brevity
  fetchDog() {
    this.controller?.abort()
    this.controller = new AbortController()
    getDog(this.props.dogId, {signal: this.controller.signal}).then(
      (dog) => {
        this.setState({dog})
      },
      (error) => {
        // handle the error
      },
    )
  }
  componentDidMount() {
    this.fetchDog()
  }
  componentDidUpdate(prevProps) {
    // handle the dogId change
    if (prevProps.dogId !== this.props.dogId) {
      this.fetchDog()
    }
  }
  componentWillUnmount() {
    // cancel the request on unmount
    this.controller?.abort()
  }
  render() {
    return <div>{/* render dog's info */}</div>
  }
}

function DogInfo({dogId}) {
  const controllerRef = React.useRef(null)
  const [dog, setDog] = React.useState(null)
  function fetchDog() {
    controllerRef.current?.abort()
    controllerRef.current = new AbortController()
    getDog(dogId, {signal: controllerRef.current.signal}).then(
      (d) => setDog(d),
      (error) => {
        // handle the error
      },
    )
  }
  // didMount
  React.useEffect(() => {
    fetchDog()
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])
  // didUpdate
  const previousDogId = usePrevious(dogId)
  useUpdate(() => {
    if (previousDogId !== dogId) {
      fetchDog()
    }
  })
  // willUnmount
  React.useEffect(() => {
    return () => {
      controllerRef.current?.abort()
    }
  }, [])
  return <div>{/* render dog's info */}</div>
}
function usePrevious(value) {
  const ref = useRef()
  useEffect(() => {
    ref.current = value
  }, [value])
  return ref.current
}

function DogInfo({dogId}) {
  const [dog, setDog] = React.useState(null)
  React.useEffect(() => {
    const controller = new AbortController()
    getDog(dogId, {signal: controller.signal}).then(
      (d) => setDog(d),
      (error) => {
        // handle the error
      },
    )
    return () => controller.abort()
  }, [dogId])
  return <div>{/* render dog's info */}</div>
}

The question is not "when does this effect run" the question is "with which state does this effect synchronize with"

useEffect(fn) // all state

useEffect(fn, []) // no state

useEffect(fn, [these, states])

class ChatFeed extends React.Component {
  componentDidMount() {
    this.subscribeToFeed()
    this.setDocumentTitle()
    this.subscribeToOnlineStatus()
    this.subscribeToGeoLocation()
  }
  componentWillUnmount() {
    this.unsubscribeFromFeed()
    this.restoreDocumentTitle()
    this.unsubscribeFromOnlineStatus()
    this.unsubscribeFromGeoLocation()
  }
  componentDidUpdate(prevProps, prevState) {
    // ... compare props and re-subscribe etc.
  }
  render() {
    return <div>{/* chat app UI */}</div>
  }
}

function ChatFeed() {
  React.useEffect(() => {
    // subscribe to feed
    // set document title
    // subscribe to online status
    // subscribe to geo location
    return () => {
      // unsubscribe from feed
      // restore document title
      // unsubscribe from online status
      // unsubscribe from geo location
    }
  })
  return <div>{/* chat app UI */}</div>
}

function ChatFeed() {
  React.useEffect(() => {
    // subscribe to feed
    return () => {
      // unsubscribe from feed
    }
  })
  React.useEffect(() => {
    // set document title
    return () => {
      // restore document title
    }
  })
  React.useEffect(() => {
    // subscribe to online status
    return () => {
      // unsubscribe from online status
    }
  })
  React.useEffect(() => {
    // subscribe to geo location
    return () => {
      // unsubscribe from geo location
    }
  })
  return <div>{/* chat app UI */}</div>
}

function ChatFeed() {
  // NOTE: this is pseudo-code,
  // you'd likely need to pass values and assign return values
  useFeedSubscription()
  useDocumentTitle()
  useOnlineStatus()
  useGeoLocation()
  return <div>{/* chat app UI */}</div>
}

// before. Don't do this!
function DogInfo({dogId}) {
  const [dog, setDog] = React.useState(null)
  const controllerRef = React.useRef(null)
  const fetchDog = React.useCallback((dogId) => {
    controllerRef.current?.abort()
    controllerRef.current = new AbortController()
    return getDog(dogId, {signal: controller.signal}).then(
      (d) => setDog(d),
      (error) => {
        // handle the error
      },
    )
  }, [])
  React.useEffect(() => {
    fetchDog(dogId)
    return () => controller.current?.abort()
  }, [dogId, fetchDog])
  return <div>{/* render dog's info */}</div>
}

function DogInfo({dogId}) {
  const [dog, setDog] = React.useState(null)
  React.useEffect(() => {
    const controller = new AbortController()
    getDog(dogId, {signal: controller.signal}).then(
      (d) => setDog(d),
      (error) => {
        // handle the error
      },
    )
    return () => controller.abort()
  }, [dogId])
  return <div>{/* render dog's info */}</div>
}

Conclusion

When Dan Abramov introduced hooks like useEffect, he compared React components to atoms and hooks to electrons.
They're a pretty low-level primitive, and that's what makes them so powerful.
The beauty of this primitive is that nicer abstractions can be built on top of these hooks which is frankly something we struggled with before hooks.
Since the release of hooks, we've seen an explosion of innovation and progress of good ideas and libraries built on top of this primitive which ultimately helps us develop better apps.

当丹·阿布拉莫夫(Dan Abramov)引入类似于useEffect的钩子时,他将React组件与原子相比较,并将钩子与电子相比较。
它们是一个非常低级的基元,这就是使它们如此强大的原因。
此原语的优点在于,可以在这些钩子之上构建更好的抽象,坦率地说,这是我们在使用钩子之前就遇到的难题。
自从钩子发布以来,我们已经看到了创新的飞速发展,以及在此原始基础之上构建的好主意和库,这些最终有助于我们开发更好的应用程序。

refs

https://epicreact.dev/myths-about-useeffect/



©xgqfrms 2012-2020

www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!


posted @ 2020-10-11 10:09  xgqfrms  阅读(184)  评论(1编辑  收藏  举报