Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert functional component using hooks to class component

I'm trying to challenge myself to convert my course project that uses hooks into the same project but without having to use hooks in order to learn more about how to do things with class components. Currently, I need help figuring out how to replicate the useCallback hook within a normal class component. Here is how it is used in the app.

export const useMovieFetch = movieId => {
    const [state, setState] = useState({});
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(false);

    const fetchData = useCallback(async () => {
        setError(false);
        setLoading(true);

        try{
            const endpoint = `${API_URL}movie/${movieId}?api_key=${API_KEY}`;
            const result = await(await fetch(endpoint)).json();
            const creditsEndpoint = `${API_URL}movie/${movieId}/credits?api_key=${API_KEY}`;
            const creditsResult = await (await fetch(creditsEndpoint)).json();
            const directors = creditsResult.crew.filter(member => member.job === 'Director');
            
            setState({
                ...result,
                actors: creditsResult.cast,
                directors
            });

        }catch(error){
            setError(true);
            console.log(error);
        }
        setLoading(false);
    }, [movieId])

useEffect(() => {
        if(localStorage[movieId]){
            // console.log("grabbing from localStorage");
            setState(JSON.parse(localStorage[movieId]));
            setLoading(false);
        }else{
            // console.log("Grabbing from API");
            fetchData();
        }
    }, [fetchData, movieId])

    useEffect(() => {
        localStorage.setItem(movieId, JSON.stringify(state));
    }, [movieId, state])

    return [state, loading, error]
}

I understand how to replicate other hooks such as useState and useEffect but I'm struggling to find the answer for the alternative to useCallback. Thank you for any effort put into this question.

like image 765
albert_anthony6 Avatar asked Feb 21 '20 18:02

albert_anthony6


2 Answers

TL;DR

In your specific example useCallback is used to generate a referentially-maintained property to pass along to another component as a prop. You do that by just creating a bound method (you don't have to worry about dependencies like you do with hooks, because all the dependencies are maintained on your instance as props or state.

class Movie extends Component {

    constructor() {
        this.state = {
            loading:true,
            error:false,
        }
    }

    fetchMovie() {
        this.setState({error:false,loading:true});

        try {
            // await fetch
            this.setState({
                ...
            })
        } catch(error) {
            this.setState({error});
        }
    }

    fetchMovieProp = this.fetchMovie.bind(this); //<- this line is essentially "useCallback" for a class component

    render() {
        return <SomeOtherComponent fetchMovie={this.fetchMovieProp}/>
    }

}

A bit more about hooks on functional vs class components

The beautiful thing about useCallback is, to implement it on a class component, just declare an instance property that is a function (bound to the instance) and you're done.

The purpose of useCallback is referential integrity so, basically, your React.memo's and React.PureComponent's will work properly.

const MyComponent = () => {
  const myCallback = () => { ... do something };
  return <SomeOtherComponent myCallback={myCallback}/> // every time `MyComponent` renders it will pass a new prop called `myCallback` to `SomeOtherComponent`
}
const MyComponent = () => {
  const myCallback = useCallback(() => { ... do something },[...dependencies]);
  return <SomeOtherComponent myCallback={myCallback}/> // every time `MyComponent` renders it will pass THE SAME callback to `SomeOtherComponent` UNLESS one of the dependencies changed
}

To replicate useCallback in class components you don't have to do anything:

class MyComponent extends Component {

   method() { ... do something }

   myCallback = this.method.bind(this); <- this is essentially `useCallback`

   render() {
     return <SomeOtherComponent myCallback={this.myCallback}/> // same referential integrity as `useCallback`
   }

}

THE BIG ONE LINER

You'll find that hooks in react are just a mechanism to create instance variables (hint: the "instance" is a Fiber) when all you have is a function.

like image 190
Adam Avatar answered Oct 22 '22 03:10

Adam


You can replicate the behavior ofuseCallback by using a memorized function for the given input(eg: movieId)

You can use lodash method

for more in-depth understanding check here

like image 43
Abhishek Avatar answered Oct 22 '22 02:10

Abhishek