Reputation: 923
I am using a function (getActorInfo()) in react to grab info from an api and set that in a State. It works but the function wont stop running.
export default function ActorProfile({ name, img, list, id, getActorInfo }) {
const [showList, setShowList] = useState(false);
const [actorInfo, setActorInfo] = useState({});
getActorInfo(id).then(val => setActorInfo(val));
console.log(actorInfo)
return (
<Wrapper>
<Actor
id={id}
name={name}
img={img}
onClick={() => {
setShowList(!showList);
}}
actorBirthday={actorInfo.actorBirthday}
/>
{showList && <MovieList list={list} actorInfo={actorInfo} />}
</Wrapper>
);
}
I tried using useEffect like this
useEffect(() => {
getActorInfo(id).then(val => setActorInfo(val));
}, {});
But I get an error that I do not understand
Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. in ActorProfile (at App.js:60)
My question is how to have this function only run once?
Upvotes: 7
Views: 20346
Reputation: 190
you need to cleanup useEffect like
useEffect(() => {
getActorInfo(id).then(val => setActorInfo(val));
return () => {
setActorInfo({});
}
},[]);
have a look at this article. It explains you why to cleanup useEffect.
Upvotes: 1
Reputation: 39320
You are still not checking if the component is mounted before you set the state. You can use a custom hook for that:
const useIsMounted = () => {
const isMounted = useRef(false);
useEffect(() => {
isMounted.current = true;
return () => (isMounted.current = false);
}, []);
return isMounted;
};
Then in your component you can do:
const isMounted = useIsMounted();
useEffect(() => {
getActorInfo(id).then(
val => isMounted && setActorInfo(val)
);
}, [getActorInfo, id, isMounted]);
Upvotes: 4
Reputation: 14385
Anything in a functional component body will run every render. Changing to a useEffect
is the correct solution to this problem.
It isn't working for you because useEffect
takes an array as its second parameter, not an object. Change it to []
, and it will only run once.
useEffect(() => {
getActorInfo(id).then(val => setActorInfo(val));
}, []);
This will be equivalent to the class-based componentDidMount
.
If your hook has a dependency, you add it to the array. Then the effect will check to see if anything in your dependency array has changed, and only run the hook if it has.
useEffect(() => {
// You may want to check that id is truthy first
if (id) {
getActorInfo(id).then(val => setActorInfo(val));
}
}, [id]);
The resulting effect will be run anytime id
changes, and will only call getActorInfo
if id
is truthy. This is an equivalent to the class-based componentDidMount
and componentDidUpdate
.
You can read more about the useEffect
hook here.
Upvotes: 7