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 发布文章使用:只允许注册用户才可以访问!
本文首发于博客园,作者:xgqfrms,原文链接:https://www.cnblogs.com/xgqfrms/p/13789461.html
未经授权禁止转载,违者必究!