How SwiftUI StateObject and React useState defer initial value evaluation

Issue #991

When building apps in SwiftUI or React, we often need to store values that change — like a counter, user input, or a list of items.

To do this:

  • React gives us useState.
  • SwiftUI gives us @State and @StateObject.

But here’s something cool:
SwiftUI’s @StateObject doesn’t create your object right away.
It waits until it’s needed — kind of like how React lets you delay things with a function.

Let’s look at how this works.


React: useState

In React, you write:

const [count, setCount] = useState(0);

This sets the first value to 0.

Now imagine this:

const [data, setData] = useState(fetchSomething());

fetchSomething() runs every time the component renders, even though the value is only used once.

React lets you fix this with a function:

const [data, setData] = useState(() => fetchSomething());

Now fetchSomething() runs only once, on the first render.
This is called lazy initialization — it waits until needed.


SwiftUI: @StateObject

In SwiftUI, we often write:

@StateObject var viewModel = MyViewModel()

It looks like MyViewModel() runs right away. But that’s not what really happens!

init(wrappedValue thunk: @autoclosure @escaping () -> ObjectType)

Thanks to something in Swift called @autoclosure, SwiftUI delays creating MyViewModel.

That means it only creates the object once, when the view appears.

You don’t need to write extra code — SwiftUI handles it for you!

Written by

I’m open source contributor, writer, speaker and product maker.

Start the conversation