We use cookies to enhance your experience on the site
CodeWorlds

Advanced State - Comments and Like System

Time to add advanced features like comments, likes, and photo upload (simulated).

Multiple State Management

Instagram has many states working together:

  • List of posts
  • Comments for each post
  • Likes
  • Modal status

Data structure:

1interface Comment {
2  id: number;
3  author: string;
4  avatar: string;
5  text: string;
6  timestamp: Date;
7}
8
9interface Post {
10  id: number;
11  imageUrl: string;
12  caption: string;
13  author: string;
14  avatar: string;
15  likes: number;
16  isLiked: boolean;
17  comments: Comment[];
18  timestamp: Date;
19}

Complete Instagram Post Component

1'use client';
2
3import { useState } from 'react';
4import Image from 'next/image';
5
6interface PostProps {
7  post: Post;
8  onLike: (postId: number) => void;
9  onAddComment: (postId: number, comment: string) => void;
10}
11
12export default function InstagramPost({ post, onLike, onAddComment }: PostProps) {
13  const [commentText, setCommentText] = useState('');
14  const [showAllComments, setShowAllComments] = useState(false);
15
16  const handleSubmitComment = (e: React.FormEvent) => {
17    e.preventDefault();
18    if (commentText.trim()) {
19      onAddComment(post.id, commentText);
20      setCommentText('');
21    }
22  };
23
24  const displayedComments = showAllComments
25    ? post.comments
26    : post.comments.slice(0, 2);
27
28  return (
29    <div className="bg-white border rounded-lg mb-4 max-w-2xl mx-auto">
30      {/* Header */}
31      <div className="flex items-center gap-3 p-4">
32        <div className="text-2xl">{post.avatar}</div>
33        <div className="flex-1">
34          <h3 className="font-bold text-sm">{post.author}</h3>
35          <p className="text-xs text-gray-500">
36            {post.timestamp.toLocaleDateString('en-US')}
37          </p>
38        </div>
39        <button className="text-gray-600 hover:text-gray-800">•••</button>
40      </div>
41
42      {/* Image */}
43      <div className="relative aspect-square">
44        <Image
45          src={post.imageUrl}
46          alt={post.caption}
47          fill
48          className="object-cover"
49        />
50      </div>
51
52      {/* Actions */}
53      <div className="p-4">
54        <div className="flex items-center gap-4 mb-3">
55          <button
56            onClick={() => onLike(post.id)}
57            className={`text-2xl transition-transform hover:scale-110 ${
58              post.isLiked ? 'text-red-500' : 'text-gray-700'
59            }`}
60          >
61            {post.isLiked ? '❤️' : '🤍'}
62          </button>
63          <button className="text-2xl hover:scale-110 transition-transform">
64            💬
65          </button>
66          <button className="text-2xl hover:scale-110 transition-transform">
67            📤
68          </button>
69          <button className="ml-auto text-2xl hover:scale-110 transition-transform">
70            🔖
71          </button>
72        </div>
73
74        {/* Likes count */}
75        <p className="font-bold text-sm mb-2">{post.likes} likes</p>
76
77        {/* Caption */}
78        <p className="text-sm mb-2">
79          <span className="font-bold">{post.author}</span> {post.caption}
80        </p>
81
82        {/* Comments */}
83        <div className="space-y-2">
84          {post.comments.length > 2 && !showAllComments && (
85            <button
86              onClick={() => setShowAllComments(true)}
87              className="text-sm text-gray-500 hover:text-gray-700"
88            >
89              View all comments ({post.comments.length})
90            </button>
91          )}
92
93          {displayedComments.map((comment) => (
94            <div key={comment.id} className="text-sm">
95              <span className="font-bold">{comment.author}</span>{' '}
96              {comment.text}
97            </div>
98          ))}
99        </div>
100
101        {/* Add Comment */}
102        <form onSubmit={handleSubmitComment} className="mt-3 flex items-center gap-2 border-t pt-3">
103          <input
104            type="text"
105            value={commentText}
106            onChange={(e) => setCommentText(e.target.value)}
107            placeholder="Add a comment..."
108            className="flex-1 outline-none text-sm"
109          />
110          <button
111            type="submit"
112            disabled={!commentText.trim()}
113            className="text-blue-500 font-bold text-sm disabled:text-blue-300 disabled:cursor-not-allowed"
114          >
115            Post
116          </button>
117        </form>
118      </div>
119    </div>
120  );
121}

Main Feed Component - Managing All Posts

1'use client';
2
3import { useState } from 'react';
4import InstagramPost from './InstagramPost';
5
6export default function TreasureFeed() {
7  const [posts, setPosts] = useState<Post[]>([
8    {
9      id: 1,
10      imageUrl: '/treasure-1.jpg',
11      caption: 'Found a massive treasure on the island! 💰',
12      author: 'Captain Redbeard',
13      avatar: '🏴‍☠️',
14      likes: 42,
15      isLiked: false,
16      comments: [
17        {
18          id: 1,
19          author: 'Morgan',
20          avatar: '⚓',
21          text: 'Amazing! Where did you find it?',
22          timestamp: new Date(),
23        },
24      ],
25      timestamp: new Date(),
26    },
27  ]);
28
29  const handleLike = (postId: number) => {
30    setPosts(posts.map(post =>
31      post.id === postId
32        ? {
33            ...post,
34            likes: post.isLiked ? post.likes - 1 : post.likes + 1,
35            isLiked: !post.isLiked,
36          }
37        : post
38    ));
39  };
40
41  const handleAddComment = (postId: number, commentText: string) => {
42    setPosts(posts.map(post =>
43      post.id === postId
44        ? {
45            ...post,
46            comments: [
47              ...post.comments,
48              {
49                id: Date.now(),
50                author: 'You',
51                avatar: '👤',
52                text: commentText,
53                timestamp: new Date(),
54              },
55            ],
56          }
57        : post
58    ));
59  };
60
61  return (
62    <div className="min-h-screen bg-gray-50 py-8">
63      <div className="max-w-2xl mx-auto">
64        <h1 className="text-2xl font-bold mb-6 px-4">Treasure Feed</h1>
65
66        {posts.map(post => (
67          <InstagramPost
68            key={post.id}
69            post={post}
70            onLike={handleLike}
71            onAddComment={handleAddComment}
72          />
73        ))}
74      </div>
75    </div>
76  );
77}

Upload Photo Form (Simulated)

1'use client';
2
3import { useState } from 'react';
4
5interface UploadFormProps {
6  onUpload: (caption: string, imageUrl: string) => void;
7}
8
9export default function UploadPhotoForm({ onUpload }: UploadFormProps) {
10  const [caption, setCaption] = useState('');
11  const [previewUrl, setPreviewUrl] = useState('');
12  const [isOpen, setIsOpen] = useState(false);
13
14  // Simulated upload - in a real app you'd use FileReader or upload to a server
15  const handleImageSelect = () => {
16    // For demo we'll use a random image from Unsplash
17    const randomId = Math.floor(Math.random() * 1000);
18    setPreviewUrl(`https://picsum.photos/seed/${randomId}/600/600`);
19  };
20
21  const handleSubmit = (e: React.FormEvent) => {
22    e.preventDefault();
23    if (caption && previewUrl) {
24      onUpload(caption, previewUrl);
25      setCaption('');
26      setPreviewUrl('');
27      setIsOpen(false);
28    }
29  };
30
31  return (
32    <>
33      <button
34        onClick={() => setIsOpen(true)}
35        className="fixed bottom-8 right-8 bg-blue-600 text-white w-16 h-16 rounded-full shadow-lg hover:bg-blue-700 transition-colors text-2xl"
36      >
37        +
38      </button>
39
40      {isOpen && (
41        <div className="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4">
42          <div className="bg-white rounded-lg max-w-md w-full p-6">
43            <h2 className="text-xl font-bold mb-4">Add a New Photo 📸</h2>
44
45            <form onSubmit={handleSubmit} className="space-y-4">
46              {!previewUrl ? (
47                <button
48                  type="button"
49                  onClick={handleImageSelect}
50                  className="w-full h-64 border-2 border-dashed border-gray-300 rounded-lg flex items-center justify-center hover:border-blue-500 transition-colors"
51                >
52                  <div className="text-center">
53                    <div className="text-4xl mb-2">📷</div>
54                    <p className="text-gray-600">Click to select a photo</p>
55                  </div>
56                </button>
57              ) : (
58                <div className="relative aspect-square rounded-lg overflow-hidden">
59                  <img src={previewUrl} alt="Preview" className="w-full h-full object-cover" />
60                  <button
61                    type="button"
62                    onClick={() => setPreviewUrl('')}
63                    className="absolute top-2 right-2 bg-white rounded-full w-8 h-8 flex items-center justify-center shadow-lg"
64                  >
6566                  </button>
67                </div>
68              )}
69
70              <textarea
71                value={caption}
72                onChange={(e) => setCaption(e.target.value)}
73                placeholder="Write a caption..."
74                className="w-full p-3 border rounded-lg resize-none outline-none focus:ring-2 focus:ring-blue-500"
75                rows={3}
76              />
77
78              <div className="flex gap-2">
79                <button
80                  type="button"
81                  onClick={() => {
82                    setIsOpen(false);
83                    setPreviewUrl('');
84                    setCaption('');
85                  }}
86                  className="flex-1 px-4 py-2 border rounded-lg hover:bg-gray-50"
87                >
88                  Cancel
89                </button>
90                <button
91                  type="submit"
92                  disabled={!caption || !previewUrl}
93                  className="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed"
94                >
95                  Publish
96                </button>
97              </div>
98            </form>
99          </div>
100        </div>
101      )}
102    </>
103  );
104}

Summary 🎓

Multiple state - managing multiple states (posts, likes, comments) ✅ Nested updates - updating objects within arrays ✅ Controlled inputs - value + onChange ✅ Conditional rendering - showing comments ✅ Event delegation - onLike, onAddComment as props ✅ Optimistic UI - instant UI updates ✅ Form handling - upload form with preview ✅ Modal patterns - floating action button + modal

You now have a complete Instagram clone with comments and likes! 🎉

See you there! 🚀

Go to CodeWorlds