Skip to main content

Simple Color example

In this example, we have a simple component that allows us to change the color of the text by clicking on different buttons. We use the useState hook to manage the current color state, and we update it when a button is clicked.

import { useState } from 'react'

const ColorExample = () => {
const [color, setColor] = useState('red')

return (
<div>
<h2 style={{ color: color }}>
Current Color: {color}
</h2>

<button onClick={() => setColor('red')}>
Red
</button>

<button onClick={() => setColor('green')}>
Green
</button>

<button onClick={() => setColor('blue')}>
Blue
</button>
</div>
)
}

export default ColorExample

Example with an object as state

In this example, we have a component that manages a person's profile information using an object as state.

import { useState } from 'react'

interface User {
name: string
age: number
favoriteColor: string
}

const UserExample = () => {

const [user, setUser] = useState<User>({
name: 'Alicia',
age: 30,
favoriteColor: 'blue',
})

const updateName = () => {
setUser((current) => ({
...current,
name: 'Alice',
}))
}

const updateAge = () => {
setUser((current) => ({
...current,
age: current.age + 1,
}))
}

const updateColor = () => {
setUser((current) => ({
...current,
favoriteColor: 'green',
}))
}

return (
<div>
<h2>{user.name}</h2>
<p>Age: {user.age}</p>
<p>Favorite Color: {user.favoriteColor}</p>

<button onClick={updateName}>
Change Name
</button>

<button onClick={updateAge}>
Increase Age
</button>

<button onClick={updateColor}>
Change Color
</button>
</div>
)
}

export default UserExample

Passing state and setter to child components

A common React pattern is to keep the state in the parent component and pass both the value and the setter to a child.

Parent Component

import { useState } from 'react'
import UserEditor from './UserEditor'

interface User {
name: string
age: number
}

const App = () => {
const [user, setUser] = useState<User>({
name: 'Alicia',
age: 30,
})

return (
<div>
<h1>User Profile</h1>

<p>Name: {user.name}</p>
<p>Age: {user.age}</p>

<UserEditor
user={user}
setUser={setUser}
/>
</div>
)
}

export default App

Child Component

import { Dispatch, SetStateAction } from 'react'

interface User {
name: string
age: number
}

interface UserEditorProps {
user: User
setUser: Dispatch<SetStateAction<User>>
}

const UserEditor = ({
user,
setUser,
}: UserEditorProps) => {

const updateName = () => {
setUser((current) => ({
...current,
name: 'Alice',
}))
}

const increaseAge = () => {
setUser((current) => ({
...current,
age: current.age + 1,
}))
}

return (
<div>
<button onClick={updateName}>
Change Name
</button>

<button onClick={increaseAge}>
Increase Age
</button>
</div>
)
}

export default UserEditor

Alternative: Pass Specialized Handlers

Instead of exposing the entire setter, you can pass only the actions the child needs:

<UserEditor
user={user}
onNameChange={(name) =>
setUser((current) => ({
...current,
name,
}))
}
/>
interface UserEditorProps {
user: User
onNameChange: (name: string) => void
}

const UserEditor = ({
user,
onNameChange,
}: UserEditorProps) => (
<button onClick={() => onNameChange('Alice')}>
Change Name
</button>
)

For small applications passing setUser directly is perfectly fine. For larger applications, passing explicit handlers is usually preferred because it gives the parent component more control over how state can be modified.