Merhabalar.

Bu yazımla beraber Post metodu ile API’a veri göndermeyi ve Post metodunun yaptığı işleri yapmayı öğreneceğiz.

Get istekleri boyunca oluşturduğumuz Blog Post uygulamasına, bir post ekleme alanı ekleyeceğiz. Post’un bizden istediği değerler var bunları Form’dan alıp, Post isteği atarak datayı göndermeyi öğreneceğiz. Sonra gelen cevap üzerinden elimizdeki var olan Post listesini güncelleyeceğiz.

Güncelleme sonrası uygulamamız şuna dönüşecek.

Amacımız

Yapacağımız işlemler şunlar olacak; öncelikle bir Add Post isminde bir buton koyacağız. Alt kısma post ekleme formunu koyacağız. Başlangıçta burası kapalı gelecek. Add Post butonuna tıklayınca alan açılacak.

Alanın içinde Title ve Body bilgilerini alacağımız alanlar ve bunları Post isteği ile gönderecek Add Post butonu olacak. Bu alanı kapatmak için alanın sağ üst köşesine close butonu koyacağız. Tıkladığımızda alan tekrar gizlenecek.

Geliştirme Kodlarımız

BlogPost.jsx

import React, { useState } from 'react'
import './BlogPost.css'
import loadingGif from "../../Assets/Images/loading.gif"

const BlogPost = () => {

    const [posts, setPosts] = useState([]);
    const [isLoading, setIsLoading] = useState(false);
    const [isData, setIsData] = useState(false);
    const [isError, setIsError] = useState(null);
    const [showError, setShowError] = useState(false);
    const [searchId, setSearchId] = useState('');
    const [comments, setComments] = useState([]);
    const [isAddSection, setIsAddSection] = useState(false);
    const [title, setTitle] = useState('');
    const [body, setBody] = useState('');


    const loadPosts = async () => {

        try {

            setComments([]);
            setIsLoading(true);

            const response = await fetch('https://jsonplaceholder.typicode.com/posts');

            if (response.ok) {

                const data = await response.json();

                setPosts(data);

                setIsLoading(false);

                if (data.length === 0 ? setIsData(false) : setIsData(true));

                openMessageModal(`${data.length} posts found`);

            } else {

                setIsLoading(false);
                setIsData(false);
                openMessageModal('There was an error!');
            }

        } catch (error) {

            setIsData(false);
            openMessageModal('There was an error!');
        }
    };

    const loadComments = async (id) => {

        try {

            setIsLoading(true);

            const response = await fetch(`https://jsonplaceholder.typicode.com/comments?postId=${id}`);

            if (response.ok) {

                const data = await response.json();

                setComments(data);

                setIsLoading(false);

                if (data.length === 0 ? setIsData(false) : setIsData(true));

                openMessageModal(`${data.length} comments found`);

            } else {

                setIsLoading(false);

                setIsData(false);

                openMessageModal('There was an error!');

            }

        } catch (error) {

            setIsData(false);

            openMessageModal('There was an error!');

        }

    };

    const openMessageModal = (info) => {

        setIsError(info);
        setShowError(true);
        setTimeout(() => setShowError(false), 5000);
    };

    const getSearchById = async () => {

        try {

            setComments([]);
            if (searchId === '') {
                loadPosts();
            }

            else {
                const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${searchId}`);

                if (response.ok) {

                    const data = await response.json();
                    setPosts([data]);

                    setIsLoading(false);

                    if (data.length === 0 ? setIsData(false) : setIsData(true));

                    if ([data].length === 0) {
                        openMessageModal('Data not found');
                    }
                    openMessageModal(`${[data].length} posts found`);

                } else {
                    setPosts([]);
                    openMessageModal('Data not found');
                }

            }



        } catch (error) {
            console.error('There was an error!', error);
        }
    }

    const handleSearch = (e) => {

        if (e.target.value === '') {
            loadPosts();
        }

        setSearchId(parseInt(e.target.value));
    }

    const addPost = async (event) => {

        event.preventDefault();
        
        const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
            method: 'POST',
            body: JSON.stringify({
                title: title,
                body: body,
                userId: 1,
            }),
            headers: {
                'Content-type': 'application/json; charset=UTF-8',
            },
        });
        const data = await response.json();
        setPosts([data, ...posts]);
        setTitle('');
        setBody('');
    };


    return (
        <div className="blogPost">

            {showError && <div className="error">{isError}</div>}

            <div className='search'>

                <input type="number" name="searchId" value={searchId} onChange={handleSearch} placeholder="Search by ID" />
                <button type='button' onClick={getSearchById}>Search</button>
                <button onClick={() => setIsAddSection(true)} >Add Post</button>
                <button onClick={loadPosts}>Load Posts</button>


            </div>

            {isAddSection &&

                <form onSubmit={addPost}>

                    <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Title" required />
                    <textarea value={body} onChange={(e) => setBody(e.target.value)} placeholder="Body" required />
                    
                    <button type="submit">Add Post</button>

                    <button className='close' type="button" onClick={ () => setIsAddSection(false)}>x</button>
                </form>
            }

            {isLoading &&
                <div className="center">
                    <img src={loadingGif} width={100} alt="Loading" />
                </div>
            }

            {!isData &&

                <div className="center">
                    <h3>There is no data</h3>
                </div>
            }

            {isData &&

                <div className="post-container">

                    <div className='posts'>

                        <h2>Posts</h2>

                        {isData &&
                            posts.map((post) => (
                                <div key={post.id} className="post">
                                    <h2>{post.title}</h2>
                                    <p>{post.body}</p>

                                    <button onClick={() => loadComments(post.id)}>Load Comments</button>

                                </div>
                            ))
                        }

                    </div>

                    <div className='comments'>

                        <h2>Comments</h2>

                        {
                            comments.map((comment) => (
                                <div key={comment.id} className="comment">
                                    <h3>{comment.name}</h3>
                                    <p>{comment.body}</p>
                                    <p>{comment.email}</p>
                                </div>
                            ))
                        }

                    </div>

                </div>
            }
        </div>
    );
}

export default BlogPost

BlogPost.css

.blogPost {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
}

button {
  padding: 10px 20px;
  margin: 20px 0;
  border: none;
  border-radius: 5px;
  background-color: #007BFF;
  color: white;
  cursor: pointer;
}

.center {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  color: #ffffff;
}

.error {
  width: 300px;
  height: 50px;
  border-radius: 20px;
  position: fixed;
  top: 20px;
  right: 20px;
  padding: 10px;
  background-color: rgb(255, 0, 0);
  color: white;
  display: flex;
  font-size: 15px;
  font-weight: bold;
  justify-content: center;
  align-items: center;
  z-index: 100;
}

.search {
  margin-top: 20px;
  display: flex;
  direction: column;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 20px;
  width: 50%;
}

.search input {
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  flex-grow: 1;
  margin-right: 10px;
}


.search input, .search button {
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
}

.search button {
  padding: 10px;
  border: none;
  background-color: #007BFF;
  color: white;
  cursor: pointer;
  flex-basis: 10%;
  margin-right: 5px;
  
}

.search button:hover {
  background-color: #0056b3;
}

.post-container {
  display: flex;
  direction: column;
  justify-content: space-between;
  flex-wrap: wrap;
  border-radius: 20px;
  
  
}

.posts {

  flex-basis: 70%;
  color: #ffffff;
  padding: 20px;
  
}

.post button{
  position:absolute;
  bottom:  -10px;
  right: 10px;
  padding: 4px;
}

.post button:hover{
  background-color: cadetblue;
}

.post {
  position: relative;
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 20px;  
  margin-top: 20px;
  position: relative;
  background-color: #ffffff;
  color: rgb(45, 71, 187);
  box-shadow: 0 0 10px rgba(114, 109, 109, 0.5);
}

.comments {
  flex-basis: 25%;
  width: 200px;
  color: rgb(255, 255, 255);
  margin-top: 20px;
}

.comment{
    border: 1px solid #ccc;
    border-radius: 5px;
    padding: 20px;  
    margin-top: 20px;
    position: relative;
    background-color: #ffffff;
    color: rgb(45, 71, 187);
    box-shadow: 0 0 10px rgba(114, 109, 109, 0.5);
}

form {
  position: relative;
  display: flex;
  flex-direction: column;
  margin-bottom: 20px;
  margin-top: 20px;
  border: 1px solid #ccc;
  width: 50%;
  padding: 20px;
  border-radius: 20px;
}

form input,
form textarea {
  margin-bottom: 10px;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  width: 95%;
}

form button {
  padding: 10px 20px;
  border: none;
  background-color: #007BFF;
  color: white;
  cursor: pointer;
  border-radius: 4px;
  width: 97%;
}

form button:hover {
  background-color: #1c4066;
}

.close {
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
  position: absolute;
  top: 0px;
  right: 15px;
  cursor: pointer;
  width: 30px;
  height: 30px;
  padding: 10px;
  background-color: red;
  font-size: 15px;
  font-weight: bold;
}

State Ekle

AddPost bölümünün açılıp kapanmasını isAddSection state’ini ekliyoruz. Blog Post eklerken gönderilecek title ve body state’lerini oluşturdum.

const [isAddSection, setIsAddSection] = useState(false);
const [title, setTitle] = useState('');
const [body, setBody] = useState('');

Add Post Section Jsx Kodları

Blog ekleme bölümümüz aşağıdaki gibi.

{isAddSection &&

                <form onSubmit={addPost}>

                    <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Title" required />
                    <textarea value={body} onChange={(e) => setBody(e.target.value)} placeholder="Body" required />
                    
                    <button type="submit">Add Post</button>

                    <button className='close' type="button" onClick={ () => setIsAddSection(false)}>x</button>
                </form>
            }

Burada görünürlüğünü isAddSection’ın true olmasına bağlı tuttum. Form Add Post butonuyla submit olduğunda, addPost fonksiyonu çağırılır.

Close butonu setIsAddSection state’ini false olarak güncelliyor. Alan gizleniyor.

addPost Fonksiyonu

const addPost = async (event) => {

        event.preventDefault();
        
        const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
            method: 'POST',
            body: JSON.stringify({
                title: title,
                body: body,
                userId: 1,
            }),
            headers: {
                'Content-type': 'application/json; charset=UTF-8',
            },
        });
        const data = await response.json();
        setPosts([data, ...posts]);
        setTitle('');
        setBody('');
    };

Burada form ögesi submit olduğu için sayfa yenilenir. Ancak biz sayfa yenilenmesini istemeyiz. e.preventDefault() sayfa yenilemesini engellemek için.

Post İsteği Gönderme

Bir post isteği aşağıdaki yapıda gönderilir.

const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
            method: 'POST',
            body: JSON.stringify({
                title: title,
                body: body,
                userId: 1,
            }),
            headers: {
                'Content-type': 'application/json; charset=UTF-8',
            },
        });

Bu isteği incelersek;

fetch fonksiyonu kullanılarak belirtilen URL’ye bir HTTP isteği gönderiliyor. İlk parametre isteğin gönderileceği URL’yi belirtir.

fetch fonksiyonunun ikinci parametresi bir konfigürasyon nesnesidir. method özelliği, bu isteğin bir POST isteği olduğunu belirtir. body özelliği ise isteğin gövdesini oluşturur. Burada JSON.stringify kullanılarak JavaScript nesnesi olan veri, JSON formatına dönüştürülüyor. Bu durumda, bir gönderi oluşturuluyor ve title, body ve userId alanlarıyla dolduruluyor.

İsteğin başlıkları (headers) belirleniyor. Content-type başlığı, gönderilen verinin türünü belirtir. Bu durumda, application/json olarak ayarlanmıştır. Ayrıca, karakter seti olarak UTF-8 belirtilmiştir.

fetch fonksiyonu asenkron bir fonksiyon olduğu için await ile beklenir. Bu, isteğin tamamlanmasını ve API tarafından dönen yanıtın response değişkenine atanmasını sağlar.

Geri kalan kodlarımızı artık biliyoruz.

Uygulama Test

Bu yazımızda böyleydi.

Bir sonraki yazımda görüşmek üzere.


Murat Bilginer
21 Şubat 1992'de doğdum. Endüstri Mühendisi olarak lisansımı 2016 yılında tamamladım. Industryolog Akademi - NGenius oluşumlarının kurucusuyum. Şu anda kendi şirketim Brainy Tech ile Web ve Mobil Geliştirme, AWS, Google Cloud Platform Sistemleri için DevOps, Big Data Analiz ve Görselleştirme hizmetleri sunmakta ve Online Eğitimler vermekteyiz.