I'm trying to convert a class component to functional component.
Class component: classBasedComponent.js [Original code included in this file]
Progress made by me:
Function Component: functionComponent.js [My progress]
Here is the GitHub link for reference: https://github.com/codebushi/gatsby-starter-dimension
Here are my errors:
I'm still getting errors with it. Where can I improvise?
27:11 error 'timeoutId' is not defined no-undef
You declared this variable as timeoutId
but switch between timeoutId
and timeOutId
when referring to it. In your current sandbox it is timeoutId
, so all the timeOutId
usages are flagged as undeclared. Settle on one or the other and be consistent.
45:30 error 'setWrapperRef' is not defined no-undef
and
80:28 error 'setWrapperRef' is not defined no-undef
This looks to be a function called by Main
to pass back out a ref to something that is then referenced in handleClickOutside
. Use a react ref to store the value.
const wrapperRef = React.useRef(null); // create ref to store value
...
const setWrapperRef = node => wrapperRef.current = node; // set current ref value
...
const handleClickOutside = (event) => {
if (!wrapperRef.current.contains(event.target)) { // access current ref value
if (isArticleVisible) {
handleCloseArticle();
}
}
}
...
<Main
isArticleVisible={isArticleVisible}
timeout={timeout}
articleTimeout={articleTimeout}
article={article}
onCloseArticle={handleCloseArticle}
setWrapperRef={setWrapperRef} // attach ref value updater
/>
66:23 error Unexpected use of 'location' no-restricted-globals
location
was previously received via props
, it's likely still received via props
.
<Layout location={props.location}>
Or since I see no other references to props
in your functional component, just destructure it in the component signature. If you need to access more prop values you can simply also destructure them here.
function IndexPage({ location }) { ...
12:19 warning 'setArticle' is assigned a value but never used no-unused-vars
This just means it was defined and never used, but upon inspection of the class-based component, updating the article
value occurs in handleOpenArticle
and handleCloseArticle
. So let's do the same in your functional component.
The issue here was that you were passing extra arguments to the setIsArticleVisible
updater that get ignored. useState
updaters don't shallowly merge updates and yours are declared to hold only a single value. I suggest to also use a functional state update to toggle the boolean state values.
handleOpenArticle(article) {
this.setState({
isArticleVisible: !this.state.isArticleVisible,
article
});
setTimeout(() => {
this.setState({
timeout: !this.state.timeout
});
}, 325);
setTimeout(() => {
this.setState({
articleTimeout: !this.state.articleTimeout
});
}, 350);
}
handleCloseArticle() {
this.setState({
articleTimeout: !this.state.articleTimeout
});
setTimeout(() => {
this.setState({
timeout: !this.state.timeout
});
}, 325);
setTimeout(() => {
this.setState({
isArticleVisible: !this.state.isArticleVisible,
article: ""
});
}, 350);
}
becomes
const handleOpenArticle = (article) => {
setIsArticleVisible(isArticleVisible => !isArticleVisible);
setArticle(article);
setTimeout(() => setTimeout(timeout => !timeout), 325);
setTimeout(() => setArticleTimeout(articleTimeout => !articleTimeout), 350);
};
const handleCloseArticle = (article) => {
setArticleTimeout(articleTimeout => !articleTimeout);
setTimeout(() => setTimeout(timeout => !timeout), 325);
setTimeout(() => {
setIsArticleVisible(isArticleVisible => !isArticleVisible);
setArticle("");
}, 350);
};
33:6 warning React Hook useEffect has a missing dependency: 'handleClickOutside'. Either include it or remove the dependency array react-hooks/exhaustive-deps
Normally you would want to include all the dependencies for the effect so all values are correct, but here it is clear the intention is to add the listeners when the component mounts, and remove them when unmounting. You can add an eslint-disable
comment for just that line. I suggest also moving the timeoutId
/timeOutId
declaration inside the effect since it isn't used outside it. It's also safe to pass undefined
to clearTimeout
, it can handle it, so remove the conditional check.
useEffect(() => {
const timeoutId = setTimeout(() => {
setLoading("");
}, 100);
document.addEventListener("mousedown", handleClickOutside);
return () => {
clearTimeout(timeoutId);
document.removeEventListener("mousedown", handleClickOutside);
};
// Effect triggered on component mount only
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
58:37 warning Expected '===' and instead saw '==' eqeqeq
Not sure where this logic came from, but it's replaced by the wrapperRef
error fix above.
All the above suggestions are based entirely on your self-described/reported issues and what I could see flagged in your sandbox. There could be other potential issues popping up after these are addressed (possibly because they were masked by them.