DEV Community

loading...
Cover image for Abstracting with react hooks on LSD

Abstracting with react hooks on LSD

Pathetic Geek
I can see bugs in your code but not my crush's hints
・3 min read

3. The final one, useBussinessLogic hook

Hooks are free of cost. i.e., you can make them really easily, and the only cost there is, is the cost of abstraction.

3.1 A basic useTodos hook

Now our components, they don't always just interact with the local state, most times they will be interacting with state on the server and managing async operations. And that's where the lines get blurry. So how about we put our hands in the magic pocket and try seeing if we have something that will help us?

Let's take an example of a basic to-do app, you'd be having a list of to-dos calling the APIs for getting it all that fun stuff, so let's extract it in a hook.

const useTodos = () => {
    const todos = useTodosStore()
    const [isLoading, setIsLoading] = useState(false)
    const [error, setError] = useState(null)

    const fetchTodos = useCallback(async () => {
        setIsLoading(true)

        try {
            const { data: todos } = await axios.get("/api/todos")
            setTodos(todos)
            setError(null)
        } catch(e) {
            setError(e)
        }

        setIsLoading(false)
    })

    useEffect(() => {
        fetchTodos()
    }, [fetchTodos])

    return {
        todos,
        fetch: fetchTodos,
        isLoading: false,
        error: null
    }
}
Enter fullscreen mode Exit fullscreen mode

If we need to change something, we can just change this small function, and it works everywhere as long as it returns the same object. Now we can just use this with one line of code wherever we want.

const App = () => {
    const { todos, isLoading, error } = useTodos()

    // other stuff
}
Enter fullscreen mode Exit fullscreen mode

3.2 Mutating to-dos

Now, let's say we want to toggle the state of a to-do. What do we do? We just put or hands in the custom hooks doremon pocket and bring out useToggleTodo

const useToggleTodos = () => {
    const [isLoading, setIsLoading] = useState(false)
    const [error, setError] = useState(null)

    const toggleTodo = useCallback(async todoId => {
        setIsLoading(true)

        try {
            const { data } = await axios.get(`/api/todos/${todoId}/toggle`)
            setError(null)
            setIsLoading(false)
            return data
        } catch(e) {
            setError(e)
        }

        setIsLoading(false)
    })

    return [toggleTodo, { isLoading, error }]
}
Enter fullscreen mode Exit fullscreen mode

But wait, we also need to update things in our store and oh, what about having multiple useTodos. Do we have a global store or are all instances updated separately? What about race condition? And caching?

3.3 Doing it all right

Remember, our custom hooks can use other hooks too, so let's bring in useQuery from react-query

import { useQuery } from "react-query"

const fetchTodos = () => axios.get('/api/todos').then(res => res.data())

const useTodos() => {
    const { data: todos, isLoading, error } = useQuery('todos', fetchTodos)

    return { todos, isLoading, error }
}
Enter fullscreen mode Exit fullscreen mode

And in our useToggleTodo we can use the useMutation from react-query so that our to-dos query is re-fetched whenever we toggle a to-do

import { useMutation } from "react-query"

const getToggleTodoById = todoId => axios.get(`/api/todos/${todoId}/toggle`)

const useToggleTodo = () => {
    return useMutation(getToggleTodoById, { refetchQueries: ["todos"] })
}
Enter fullscreen mode Exit fullscreen mode

Look how we moved to using vanilla axios to react-query in seconds and didn't have to change more than a couple of lines. And now we have these nice hooks for our components to hooks into.

And my friends, that how we use hooks and manage like a pro (or from what all I know at least). Now you can go show off your new shiny gadgets to your friends if you have any.

Discussion (32)

Collapse
mindplay profile image
Rasmus Schultz

I think this whole series perfectly illustrates why application state in React is a nightmare of complexity and performance pitfalls. The fact that you need all this knowledge - that all this code is necessary... Just makes me think React has a serious, fundamental design issue.

I mean, compare this to something like Sinuous, where component state is literally identical to application state: observable("value") and you're done - with precise updates and no concerns about factoring your components for performance.

State management in React is just too much work, which is why people use one of the endless list of state management libraries. The fact that these even exist points to a serious design issue in React.

Unfortunately, I don't think this is something they can or ever will solve - because they have to be backwards compatible, otherwise they lose the whole ecosystem. Not that losing all these state management libraries would be a loss, but they have to stay compatible with component libraries, which likely means they can never fix the fundamental design issues around state management.

Your article series is well written and probably helpful to a lot of people struggling with this complexity in React - but it also perfectly illustrates one of the key reasons why, personally, I'm looking at other options.

Collapse
stevenmcgown profile image
StevenMcGown

As someone coming from Angular, React seems so much more lightweight and simple. Not to mention using JavaScript on Angular is a nightmare. Maybe I'm just clueless, I'm not too much of a web developer tbh. I believe this is why React has become the tool of choice for many companies over Angular.

Collapse
patheticgeek profile image
Pathetic Geek Author

Yeah that's very true. React is way more flexible than angular js and finding reletively good react devs is easier and faster than finding good angular devs and also learning react is way easier than learning angular but the only thing new people sruggle at is state management, if you see any course or tutorial state management is like one of most dense and long parts of it. So yes react is great but state management in it still needs to be thought more about

Collapse
patheticgeek profile image
Pathetic Geek Author

Interesting take. I think if you use redux toolkit it's a breeze, it has made managing that state really easy but yeah you're right that state management is a serious issues for whoever is starting to learn react.

I just looked at sinuous and i think you might run into same problem when you share the state between multiple components. Like which component is updating the state when and predicting what state will be after some action. I might be wrong here as I just took a look at it's docs. Could you share some resource on how to manage state accross components?

Also I do know that vue and sevelte also do state management really well you may also wanna look into those.

Collapse
metammodern profile image
Bunyamin Shabanov

Redux makes you write a lot of unnecessary code. Changing one action or adding new action leads to change of several files.

Thread Thread
patheticgeek profile image
Pathetic Geek Author

Try redux toolkit, it isn't the case anymore

Thread Thread
metammodern profile image
Bunyamin Shabanov

Ok, I'll check it out. Thanks

Thread Thread
mindplay profile image
Rasmus Schultz

Redux-zero might be worth a look too... familiar stores, actions, middleware, support for Redux devtools with time-travel, etc. - just no reducers, making it quite a lot simpler. 🙂

Thread Thread
metammodern profile image
Bunyamin Shabanov

Thank you)

Thread Thread
patheticgeek profile image
Pathetic Geek Author

It still has mapDispatchToProps and stuff which I find a bit ugly. But yeah, still way better than taditional redux.

Collapse
janestop profile image
janestop

I'm still learning React, been trying to rewrite my site, and I've been struggling with this idea. Are you using hooks as a replacement for Redux and Sagas? I've seen a codebase for a basic app with authentication, using redux and sagas and it made me want to throw up. So I've been thinking about a similar way to use hooks and context so replace them. Wanted to know your thoughts. Post looks brilliant 👏 keep doing what you do chief

Collapse
patheticgeek profile image
Pathetic Geek Author

Yeah i am using it as replacement for using any state management library.
And yes the old redux stuff is really really ugly and I'm not touching it ever if i dont absolutely have to. I definately would recommend using something like I wrote in this article but if you want a bit more you can look at redux toolkit. It's way way way better than traditional redux and I've been using it for a while now and it just works great.

Collapse
janestop profile image
janestop

Honestly I'd prefer to stick to just using native react in my application where at all possible. I think if it's possible with the framework why add the dependencies. Thanks for replying, would you mind if I contacted you if I have any issues??

Thread Thread
patheticgeek profile image
Pathetic Geek Author

Same, I only add redux and stuff if my state management gets big otherwise context and reducer works well.
Yeah sure, same name on every platfrom choose whichever you prefer :)

Collapse
blackr1234 profile image
blackr1234

What is LSD? Sorry I don't understand the article title.

Collapse
reikrom profile image
Rei Krom

Judging by the past article titles, clickbait reference to a controlled substance.

Collapse
patheticgeek profile image
Pathetic Geek Author

@reikrom is right, it's a reference to this ;)

Collapse
agnic profile image
agni-c

lol

Collapse
mdovn profile image
mdovn

Hook is a double edge sword. Not only the abstraction cost but also performance. Only a small state in a hook changed and you will see that all the other hooks in the same component be re-evaluated as well. What if inside a hook inside a hook inside a hook... Which executes an expensive operation you were not aware of? Your users will feel the lagging and pitch off.
Don't get me wrong, nothing is perfect but it's too easy to do the bad thing with hook.

Collapse
patheticgeek profile image
Pathetic Geek Author

Well you can always write bad code so I guess the point should be that they are good but they should be used with care. Also i think the performance cost isn't that much and it might be the same as having those things inside the component. Can you refer to something where i can read more on the performance cost?

Collapse
mdovn profile image
mdovn

That's right, because human always (and will) make mistakes. The best we can do, in my opinion is try to design tools that make it hard to do stupid things.
You can read this article: dev.to/ryansolid/5-ways-solidjs-di...
I found the author has many good articles on the same topic which took a deep dive into reactivity of popular frontend libraries.
The above article is not exactly what I tend to share with you. I could not find the best one. I just remember it's also from same author

Thread Thread
patheticgeek profile image
Pathetic Geek Author

Yup i agree with that.
I wont perticularly switch to some other framework just because like I'm way too invested in this one and also like many companies have written stuff in these and those cant be changes as easily as i can switch frameowrk so i am more interested to just make existing stuff better and in only some cases creating new things.
I'll read the article you sent

Collapse
askoflor profile image
askoflor

hi i read somewhere that putting reactjs in a laravel project is not a good practice

Collapse
patheticgeek profile image
Pathetic Geek Author

I haven't worked that much on larvel so can't say much. But what I will say is if you're using react and php you definately want to keep your client and server seperate because in my experience php doesn't play very well if we have the client side in the same code specially some framework like react, vue etc.
So you can use react with php APIs but i wont recommend having them mixed in same project like what you might see in a traditional php project.

Collapse
askoflor profile image
askoflor

you say not to mix react and php as in a traditional project but my question is the following how to separate them and make them work in production together

Thread Thread
patheticgeek profile image
Pathetic Geek Author • Edited

So you will have 2 diffrent projects one which will be your php server and it will only serve JSON data and one will be your react app which will call the php APIs get JSON and display it.

In local you can start your php project at localhost:8081 and your react project at localhost:8080 and when you want to call any api you can call it at localhost:8081/test.

And in prod your php project will be deployed at lets say api.example.com
and react project will be deployed at example.com. So when you want to call any apis in prod you send a request to api.example.com/test.

Axios (axios-http.com/) is a good library for making this kind of requests, you can do somthing like below

// check if we're in dev and set base url correctly
const baseURL = process.env.NODE_ENV === 'development' ? 'localhost:8081' : 'api.example.com';
// create a axios instance
const axios = Axios.create({ baseURL })
// send a request
axios.get('/test').then(res => console.log(res))
Enter fullscreen mode Exit fullscreen mode

What this will do is set the correct base url and whenever you send a request it will add the base url before the path ('/test') so your requests will go to right address.

With this your server (php) and client (react) are two different things and you can easily swap react for vue or angular or swap php for ruby on rails or python.

Thread Thread
askoflor profile image
askoflor

okay with this method if we realize a web application the loading speed of the site and the seo referencing will be obtained

Thread Thread
agnic profile image
agni-c • Edited

If you are not intending to use client server architecture , It may be wise to use vue instead of react. Laravel even sponsoring vue because it is solving a lot of it's problems without much tooling like react.

Collapse
myrtletree33 profile image
jhtong • Edited

Great article, however I am unclear how you passed the option refetchQueries in useMutation. It does not seem to be an available option in the documentation? Did you mean QueryCache?

Collapse
patheticgeek profile image
Pathetic Geek Author

Hmm i used the refrence for useMutation from a jsconf video i saw a few weeks ago, myabe you can use the refetchQueries in QueryClient in the onSuccess method passed in useMutation options.
Check this link

Collapse
vjoao profile image
Victor Nascimento

Good article overall. Would you mind comparing your design choices with Recoil? It seems you would have a simpler shared state with Recoil.

Collapse
patheticgeek profile image
Pathetic Geek Author

I haven't used it yet, but sure i'll take a look and update this article if i found it interesting