We use cookies to enhance your experience on the site
CodeWorlds

State Management - useState and Interactivity

Time to add interactivity to Kraken's Call! You'll learn to manage application state and create forms.

useState - Hook for State Management

useState is a React hook that allows components to "remember" things.

Basic usage:

1'use client';
2
3import { useState } from 'react';
4
5export default function Counter() {
6  // [value, setter function] = useState(initial value)
7  const [count, setCount] = useState(0);
8
9  return (
10    <div>
11      <p>Counter: {count}</p>
12      <button onClick={() => setCount(count + 1)}>
13        +1
14      </button>
15    </div>
16  );
17}

How it works:

  1. useState(0)
    - creates state with initial value 0
  2. count
    - current value
  3. setCount
    - function to change the value
  4. When you click the button →
    setCount
    changes
    count
    → component re-renders

Different State Types

String:

1const [name, setName] = useState('');
2
3<input
4  value={name}
5  onChange={(e) => setName(e.target.value)}
6/>

Boolean:

1const [isOpen, setIsOpen] = useState(false);
2
3<button onClick={() => setIsOpen(!isOpen)}>
4  Toggle
5</button>

Array:

1const [items, setItems] = useState<string[]>([]);
2
3const addItem = (item: string) => {
4  setItems([...items, item]);
5};

Object:

1const [user, setUser] = useState({
2  name: 'Redbeard',
3  age: 35
4});
5
6setUser({ ...user, age: 36 });

Form for Adding Posts

Let's create a form for adding new posts:

1'use client';
2
3import { useState } from 'react';
4
5export default function CreatePostForm() {
6  const [content, setContent] = useState('');
7  const [charCount, setCharCount] = useState(0);
8  const MAX_LENGTH = 280;
9
10  const handleSubmit = (e: React.FormEvent) => {
11    e.preventDefault(); // Prevents page reload
12
13    if (content.trim()) {
14      console.log('New post:', content);
15      setContent(''); // Clear the form
16      setCharCount(0);
17    }
18  };
19
20  const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
21    const text = e.target.value;
22    setContent(text);
23    setCharCount(text.length);
24  };
25
26  return (
27    <form onSubmit={handleSubmit} className="bg-white p-6 rounded-lg shadow">
28      <textarea
29        value={content}
30        onChange={handleChange}
31        maxLength={MAX_LENGTH}
32        className="w-full p-4 border rounded-lg resize-none focus:ring-2 focus:ring-blue-500 outline-none"
33        rows={4}
34      />
35
36      <div className="flex items-center justify-between mt-4">
37        <span className={`text-sm ${
38          charCount > MAX_LENGTH * 0.9 ? 'text-red-500' : 'text-gray-500'
39        }`}>
40          {charCount}/{MAX_LENGTH}
41        </span>
42
43        <button
44          type="submit"
45          disabled={!content.trim() || charCount > MAX_LENGTH}
46          className="bg-blue-600 text-white px-6 py-2 rounded-lg font-semibold disabled:bg-gray-300 disabled:cursor-not-allowed hover:bg-blue-700 transition"
47        >
48          Send 🚀
49        </button>
50      </div>
51    </form>
52  );
53}

Feed with Dynamic Posts

Now let's connect the form with the list of posts:

1'use client';
2
3import { useState } from 'react';
4
5interface Post {
6  id: number;
7  author: string;
8  avatar: string;
9  content: string;
10  timestamp: Date;
11  likes: number;
12}
13
14export default function Feed() {
15  const [posts, setPosts] = useState<Post[]>([
16    {
17      id: 1,
18      author: 'Captain Redbeard',
19      avatar: '🏴‍☠️',
20      content: 'We just discovered a new island!',
21      timestamp: new Date(),
22      likes: 42,
23    },
24  ]);
25
26  const [newPostContent, setNewPostContent] = useState('');
27
28  const handleSubmit = (e: React.FormEvent) => {
29    e.preventDefault();
30
31    if (!newPostContent.trim()) return;
32
33    const newPost: Post = {
34      id: Date.now(),
35      author: 'Morgan Blackheart',
36      avatar: '⚓',
37      content: newPostContent,
38      timestamp: new Date(),
39      likes: 0,
40    };
41
42    setPosts([newPost, ...posts]); // Add to the beginning
43    setNewPostContent('');
44  };
45
46  return (
47    <div className="max-w-2xl mx-auto p-8">
48      {/* Form */}
49      <form onSubmit={handleSubmit} className="bg-white p-6 rounded-lg shadow mb-6">
50        <textarea
51          value={newPostContent}
52          onChange={(e) => setNewPostContent(e.target.value)}
53          className="w-full p-4 border rounded-lg"
54          rows={3}
55        />
56        <button
57          type="submit"
58          className="mt-4 bg-blue-600 text-white px-6 py-2 rounded-lg"
59        >
60          Send
61        </button>
62      </form>
63
64      {/* Post list */}
65      <div className="space-y-4">
66        {posts.map((post) => (
67          <div key={post.id} className="bg-white p-6 rounded-lg shadow">
68            <div className="flex items-center gap-3 mb-4">
69              <div className="text-3xl">{post.avatar}</div>
70              <div>
71                <h3 className="font-bold">{post.author}</h3>
72                <p className="text-sm text-gray-500">
73                  {post.timestamp.toLocaleTimeString('en-US')}
74                </p>
75              </div>
76            </div>
77            <p className="text-gray-800">{post.content}</p>
78            <div className="mt-4 text-gray-500">
79              ❤️ {post.likes}
80            </div>
81          </div>
82        ))}
83      </div>
84    </div>
85  );
86}

Lifting State Up - Sharing State Between Components

When two components need the same state, we move it to their common parent.

1'use client';
2
3import { useState } from 'react';
4
5// Like button component
6function LikeButton({
7  likes,
8  onLike
9}: {
10  likes: number;
11  onLike: () => void;
12}) {
13  return (
14    <button onClick={onLike} className="flex items-center gap-2">
15      ❤️ {likes}
16    </button>
17  );
18}
19
20// Like counter component
21function LikeCounter({ likes }: { likes: number }) {
22  return (
23    <div className="text-sm text-gray-500">
24      Total likes on page: {likes}
25    </div>
26  );
27}
28
29// Parent holds the state
30export default function Post() {
31  const [likes, setLikes] = useState(42);
32
33  const handleLike = () => {
34    setLikes(likes + 1);
35  };
36
37  return (
38    <div className="bg-white p-6 rounded-lg">
39      <p className="mb-4">Post content...</p>
40
41      <LikeButton likes={likes} onLike={handleLike} />
42      <LikeCounter likes={likes} />
43    </div>
44  );
45}

useEffect - Side effects

useEffect
runs code after render (e.g. fetching data, timers).

1'use client';
2
3import { useState, useEffect } from 'react';
4
5export default function Timer() {
6  const [seconds, setSeconds] = useState(0);
7
8  useEffect(() => {
9    // Run after each render
10    const interval = setInterval(() => {
11      setSeconds((s) => s + 1);
12    }, 1000);
13
14    // Cleanup - remove timer when component disappears
15    return () => clearInterval(interval);
16  }, []); // [] = run only once
17
18  return <div>Elapsed: {seconds}s</div>;
19}

Dependency array:

1// Run only once (on mount)
2useEffect(() => {
3  console.log('Component mounted');
4}, []);
5
6// Run when count changes
7useEffect(() => {
8  console.log('Count changed:', count);
9}, [count]);
10
11// Run after every render
12useEffect(() => {
13  console.log('Every render');
14}); // No []

LocalStorage - Saving State

Save posts in the browser so they don't disappear after refresh:

1'use client';
2
3import { useState, useEffect } from 'react';
4
5export default function Feed() {
6  const [posts, setPosts] = useState<Post[]>([]);
7
8  // Load from localStorage on start
9  useEffect(() => {
10    const saved = localStorage.getItem('kraken-posts');
11    if (saved) {
12      setPosts(JSON.parse(saved));
13    }
14  }, []);
15
16  // Save to localStorage when posts change
17  useEffect(() => {
18    localStorage.setItem('kraken-posts', JSON.stringify(posts));
19  }, [posts]);
20
21  // Rest of the code...
22}

Summary 🎓

useState - hook for state management ✅ Forms -

value
,
onChange
,
onSubmit
e.preventDefault() - prevents page reload ✅ Dynamic lists - adding/removing elements ✅ Lifting state up - sharing state between components ✅ useEffect - side effects (timers, fetch, localStorage) ✅ Dependency array - controls when the effect runs ✅ localStorage - saving data in the browser

Now you have a complete, interactive Kraken's Call application! 🎉

See you there! 🚀

Go to CodeWorlds