🦥Managing Data is Complicated

Hello friends!

Welcome to this week’s Sloth Bytes. I hope you had an amazing week!

🦥 No sponsor this week, just vibes.

But if you want to reach 50,000+ developers, founders, and tech lovers who actually open their emails — this is the place.

Sloth’s Goals for 2026

I know it’s almost February, but I've been thinking a lot about what I want from 2026.

  1. I want to get closer to you. Not in a weird way (relax buddy, buy me dinner first). I want this community to be for people who are curious and care about programming.

  2. I want to be more social. I almost didn't write this one because I’m not a very social person (but I do social media for a living now… Weird how things work.)

  3. Changing the Tuesday newsletter (slightly.)

    1. I’ll still cover programming concepts, but I want to also include what I'm learning and why.

    2. I also want to interview developers who are doing interesting work + their professional advice.

  4. I want to be more consistent. Yeah… this one is pretty easy to understand.

Since I shared my goals, what are your goals for 2026?

Feel free to reply to the email with your goals. I would love to hear them!

Managing Data is hard…

The first time you successfully fetch data from an API feels like pure magic.

Well, at least it was for me. I remember it vividly. It was around spring 2022 (Wow, four years, I’m really getting old…)

I was learning web dev from The Odin Project (this specific lesson), and I copied the code snippet with the Giphy API.

The moment that GIF displayed on a blank HTML page, I felt like I could build anything.

Facebook? Easy. The next YouTube? Please, that’s child’s play, I can use an API now.

That feeling lasted about an hour.

Because while I did learn how to fetch data, I now had to learn the next step.

How do you handle that data?

The Real Problem

Alright, you have your cool data. But uh… What do you do with it?

You need to handle a lot:

  • Success states (showing the data)

  • Loading states (spinners, skeletons)

  • Error states

  • Retry logic 

  • Cache invalidation (keeping data fresh)

  • Request deduplication (not fetching the same thing 10 times)

  • Authentication headers

Easy right? Of course not, it’s programming!

Now, your simple fetch() call evolves into this:

useEffect(() => {
  async function fetchData() {
    setIsLoading(true)
    setError(null)
    try {
      const response = await fetch("/api/data")
      const data = await response.json()
      setData(data)
    } catch (err) {
      setError("Something went wrong")
    } finally {
      setIsLoading(false)
    }
  }
  fetchData()
}, [])

Every React developer starts like this, and funny enough, if you read the React docs, they don’t recommend this approach for multiple reasons:

  • It doesn’t run on the server (bad for SEO and initial load times)

  • It’s easy to create “network waterfalls.”

  • Fetching like this usually means no preloading or caching (users wait every single time)

  • Not ergonomic (LOTS of boilerplate)

Great… So what’s the solution?

Well, React recommends using a framework with its own data-fetching system (like Next.js) or an open-source solution.

But why?

The Persistence Problem

When you move from one page to another, any data you fetched is gone.

To get that data again, you have a few options:

  1. Refetch it from the server (slow, expensive)

  2. Store it somewhere global (Context, Redux, Zustand, Jotai)

  3. Get fancy with HTTP cache headers (complicated)

Most people go with option 2, but now you're not just fetching data, you're now:

  • Syncing data to a store

  • Writing custom hooks

  • Managing cache lifetime logic

  • Aborting requests if components unmount mid-fetch

All for something that was supposed to be simple.

2015- 2019: The Redux Years

For years, the answer to "how do I manage fetched data?" was Redux

When I first saw Redux, it was overwhelming.

Actions, action creators, reducers, middleware, store, dispatches, and what the heck is a thunk?

Fetching data in Redux required like 50+ lines of boilerplate and multiple new files.

// Action types
const FETCH_USER_REQUEST = 'FETCH_USER_REQUEST'
const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS'
const FETCH_USER_FAILURE = 'FETCH_USER_FAILURE'

// Thunk with Axios
const fetchUser = (id) => async (dispatch) => {
  dispatch({ type: FETCH_USER_REQUEST })
  try {
    const { data } = await axios.get(`/api/users/${id}`)
    dispatch({ type: FETCH_USER_SUCCESS, payload: data })
  } catch (error) {
    dispatch({ type: FETCH_USER_FAILURE, error: error.message })
  }
}

// Reducer
const userReducer = (state = initialState, action) => {
  switch (action.type) {
    case FETCH_USER_REQUEST:
      return { ...state, loading: true }
    case FETCH_USER_SUCCESS:
      return { ...state, loading: false, data: action.payload }
    case FETCH_USER_FAILURE:
      return { ...state, loading: false, error: action.error }
    default:
      return state
  }
}

There had to be a better way, right?

2019: The Year Everything Changed

Yep, React didn’t always have hooks. It used to be class components, and they were nasty to use.

Class components vs functional

Honestly, class components were one of the main reasons for Redux's popularity.

Since components couldn't easily share stateful logic, developers threw everything into Redux just to avoid prop drilling through class components.

But hooks changed everything. They made components simpler and more composable. And a lot of developers saw the potential immediately.

One of them was Tanner Linsley.

He realized the real problem: we were confusing server state with client state.

Your server state (data from APIs) has different needs than your client state (is this modal open? which tab is selected?).

Server state needs:

  • Caching

  • Background refetching

  • Synchronization across components

  • Staleness tracking

  • Request deduplication

Client state needs:

  • Simple updates

  • Component-level scope

  • No persistence beyond the session

But we were treating them exactly the same. 

We dumped everything into Redux or some other state management tool.

React Query isn't actually a data fetching library. It doesn't fetch anything for you.

It's an async state manager that happens to be really good at managing server state.

You give it a promise, and it handles all the dirty work you shouldn't be thinking about:

  • Caching

  • Background updates

  • Request deduplication

  • Error handling

  • Keeping your UI in sync with fresh data

const { data, error, isLoading } = useQuery({
  queryKey: ['tasks'],
  queryFn: () => fetch('/api/tasks').then(res => res.json())
})

At the same time, Vercel released SWR with the same core concepts.

Both SWR and React Query exploded in popularity because they solved a pain point every React developer was feeling but couldn't quite articulate.

So did this replace Redux?

Well kinda…

You can use React Query or SWR alongside Redux (or state managers)

But once you start using React Query for server state, you realize that was 90% of why you needed Redux in the first place.

Most of what we stored in Redux or state managers was just cached server data.

The actual client state (modals, tabs, form inputs) is such a small piece that it doesn't really need a global state manager. You can just use React's built-in useState and useContext.

My recommendations for state management

1. Start with TanStack Query (or SWR) from day one.

Don't wait until your app is a mess. The setup is minimal, the payoff is massive.

2. Wrap everything in custom hooks.

useUser(), useTasks(), useProjects(). React recommends this pattern.

If you switch from REST to GraphQL later? Update the hook. Your components don't care.

3. Understand the why.

These libraries exist because thousands of developers hit the same painful edge cases. They're not overengineering, they're solving real problems you'll eventually face.

Fetching data isn't hard. Managing the state is.

In 2015, developers wrote Redux boilerplate. In 2019, React Query and SWR showed us a better way.

New tools keep emerging, so always stay flexible. I hope this small history lesson helped!

Thanks to everyone who submitted! Lots of submissions for this, so I’ll do the first 10:

Bingo Check

Create a function that takes a 5x5 2D list and returns True if it has at least one Bingo, and False If it doesn't.

Examples

bingo_check([
  [45, "x", 31, 74, 87],
  [64, "x", 47, 32, 90],
  [37, "x", 68, 83, 54],
  [67, "x", 98, 39, 44],
  [21, "x", 24, 30, 52]
])
output = True

bingo_check([
  ["x", 43, 31, 74, 87],
  [64, "x", 47, 32, 90],
  [37, 65, "x", 83, 54],
  [67, 98, 39, "x", 44],
  [21, 59, 24, 30, "x"]
]) 
output = True

bingo_check([
  ["x", "x", "x", "x", "x"],
  [64, 12, 47, 32, 90],
  [37, 16, 68, 83, 54],
  [67, 19, 98, 39, 44],
  [21, 75, 24, 30, 52]
]) 
output = True

bingo_check([
  [45, "x", 31, 74, 87],
  [64, 78, 47, "x", 90],
  [37, "x", 68, 83, 54],
  [67, "x", 98, "x", 44],
  [21, "x", 24, 30, 52]
]) 
output = False

Notes

Only check for diagonals, horizontals and verticals.

How To Submit Answers

  • Reply with a link to your solution (GitHub, link replit, etc)

  • If you’re on the web version, leave a comment!

  • If you want to be mentioned here, send a GitHub link or Replit!

That’s all from me!

Have a great week, be safe, make good choices, and have fun coding.

If I made a mistake or you have any questions, feel free to comment below or reply to the email!

See you all next week.

What'd you think of today's email?

Login or Subscribe to participate in polls.

Want to advertise in Sloth Bytes?

If your company is interested in reaching an audience of developers and programming enthusiasts, you may want to advertise with us here.

Reply

or to participate.