본문 바로가기

[React] State & Props로 간단한 트위터 추가 기능 구현하기

[React] State & Props로 간단한 트위터 추가 기능 구현하기

추가 기능

1. 특정 유저 게시글 필터 기능

2. 게시글 삭제 기능

 

1. 특정 유저 게시글 필터 기능

tweets.js
// ** Tweets.js

import React, { useState } from "react";
import Footer from '../Footer';
import Tweet from '../Components/Tweet';
import './Tweets.css';
import dummyTweets from '../static/dummyData';

const Tweets = () => {
  const [username, setUsername] = useState("Doyu 🤍");
  const [msg, setMsg] = useState("");
  const [tweets, setTweets] = useState(dummyTweets);
  
  const [filteredTweets, setFilteredTweets] = useState(dummyTweets); // 필터된 트윗 state
  const [isFiltered, setIsFiltered] = useState(false); // 필터 여부 state
  const [currentUsername, setCurrentUsername] = useState('default'); // 최근 유저명

  const handleButtonClick = (event) => {
 // 버튼을 클릭하면 트윗 글을 생성하여, tweets 배열에 넣어준다 (1탄의 내용, 생략) 
  };

  const handleChangeUser = (event) => {
    setUsername(event.target.value);
  };

  const handleChangeMsg = (event) => {
    setMsg(event.target.value);
  };

  const handleFilterTweet = (event) => {           // 셀렉트 값에 변동이 생기면 함수 호출
    if (event.target.value === 'default') {        // 기본적으로 세팅된 값 
      setTweets(tweets);                           // 디폴트 값으로 다시 돌려준다.
      setIsFiltered(false);
    } else {                                        // 특정 유저만 보기 위해 클릭을 하면 
      const filtered = tweets.filter(
        (tweet) => tweet.username === event.target.value  // 해당 유저의 이름에 해당되는 tweet만 필터링 
      );
      setIsFiltered(true);                                // 그후 필터여부 state 값을 바꿔줌 
      setFilteredTweets(filtered);                        // 필터링된 트윗글로 바꿔줌
    }
    setCurrentUsername(event.target.value);
  };

  const handleAllTweetButton = (event) => {          // 초기화 하고 싶으면 
    setIsFiltered(false);                            // 필터 안 된 false 값으로 state 세팅 
    // setTweets(tweets);
    setCurrentUsername('default');                   // 기본 값으로 다시 세팅 
  };

  const handleDeleteTweet = (username, deleteIndex) => {   // tweet 컴포넌트에서 삭제 버튼을 누를 수 있다.
    if (isFiltered) {                                      // 필터가 된 상태면
      alert('필터 시 삭제 불가합니다.')
      return;
    }
    const restTweets = tweets.filter((tweet, idx) => idx !== deleteIndex);
    setTweets(restTweets);   // deleteIndex 뺀 글들이 tweets 상태로 바뀜 
  };

 

const tweetsRenderer = (tweet, idx) => {
    return (
      <Tweet
        key={tweet.id}
        tweet={tweet}
        handleDeleteTweet={handleDeleteTweet} // tweets 상태 변경 함수를 자식에게 내려보냄
        idx={idx}
      />
    );
  }
  
  return (
    <React.Fragment>
      <div className="tweetForm__container">
        <div className="tweetForm__wrapper">
          <div className="tweetForm__profile">
            <img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlLU58%2Fbtr2QK22XA5%2FoS1IhF5MgHA3fqZdlvGM1K%2Fimg.png" />
          </div>
          <div className="tweetForm__inputContainer">
            <div className="tweetForm__inputWrapper">
              <div className="tweetForm__input">

                <input
                  type="text"
                  placeholder="your username here.."
                  className="tweetForm__input--username"
                  onChange = {handleChangeUser}
                  value={username}
                ></input>

                <textarea
                placeholder="무슨 생각을 하고 계신가요?"
                className="tweetForm__input--message"
                onChange = {handleChangeMsg}
                value={msg}
                ></textarea>

              </div>
              <div className="tweetForm__count" role="status">
                <span className="tweetForm__count__text">
                  total: {tweets.length}
                </span>
              </div>
            </div>
            <div className="tweetForm__submit">
              <div className="tweetForm__submitIcon"></div>
              <button 
              className= "tweetForm__submitButton"
              onClick={handleButtonClick}>전송</button>
            </div>
          </div>
        </div>
      </div>
      
      
      <div className="tweet__selectUser">

<!-- 셀렉터 -->
      <select value={currentUsername} onChange={handleFilterTweet}>
          <option value="default">     <!--  value가 default면 모든 트윗을 보여줌-->
            -- click to filter tweets by username --
          </option>

<!--🟣 설명(1) reduce 안에 reduce, 그후 바로 map을 써준다 -->

          {tweets.reduce((acc, cur) => {                  <!--초기값 []-->
            const isNotUnique = acc.reduce((a, c) => {    <!--초기값 false -->
              if (c.username === cur.username) {
                return true
              }
              return a === true ? true : false
            }, false)
            return isNotUnique ? acc : [...acc, cur]
            }, []).map((tweet) => {
            return (
              <option key={tweet.id} value={tweet.username}> <!--map으로 옵션값-->
                {tweet.username}                               
              </option>
            );
          })}
        </select>

 

🟣  설명(1)

이 코드는 중복되지 않은 username을 가진 tweet 객체들 중에서 username을 option value로, username을 option text로 갖는 HTML option element를 생성하는 코드이다.

1. 먼저 reduce 메서드를 사용하여 중복되지 않은 username을 가진 tweet 객체들을 추출한다. reduce 메서드는 acc와 cur 매개변수를 받는다. acc는 reduce 메서드의 첫 번째 호출에서 초기값으로 사용됩니다. 여기서는 빈 배열이다. 

2. 그리고 acc 배열에 현재 요소(cur)의 username이 있는지 확인하기 위해 reduce 메서드를 다시 사용한다. reduce 메서드에서 a와 c 매개변수를 받는다. a는 reduce 메서드의 첫 번째 호출에서 초기값으로 사용된다. 여기서는 false이다. 

3. reduce 메서드에서 현재 요소(cur)의 username이 acc 배열에 이미 존재하는지 확인하고, 이미 존재한다면 isNotUnique 변수에 true를 할당한다. 그렇지 않으면 false를 할당한다.

4. 마지막으로, isNotUnique 변수가 true인 경우, 현재 요소(cur)를 제외한 이전 배열(acc)을 반환하고, 그렇지 않으면 현재 요소(cur)를 배열에 추가하여 반환한다.

5. 마지막으로, 중복되지 않은 username을 가진 tweet 객체들을 이용하여 option element를 생성한다. map 메서드를 사용하여 HTML option element를 반환한다. 

 

<!-- currentUsername이 defualt가 아니면, 되돌려주는 버튼-->
        
        {currentUsername !== 'default' ? (
          <button onClick={handleAllTweetButton}>
            <i className="far fa-caret-square-left"></i>
          </button>
        ) : (
          <div></div>
        )}

      </div>
      
      
      <ul className="tweets">
<!-- 필터된 상태면, 필터된 트윗에서 map, 필터가 안 된 상태라면 전체 tweets에서 map -->     
<!-- tweetsRenderer 함수를 따로 만들었음 -->
       {isFiltered ? filteredTweets.map(tweetsRenderer) : tweets.map(tweetsRenderer)}
      </ul>
      
      <Footer />
    </React.Fragment>
  );
};

export default Tweets;

 


 

2. 게시글 삭제 기능

tweets.js
// tweets.js

//tweet 컴포넌트에서 삭제 버튼을 누르면 tweet.username, idx 을 인자로 받아온다.

  const handleDeleteTweet = (username, deleteIndex) => {     
    if (isFiltered) {
      alert('필터 시 삭제 불가합니다.')
      return;
    }
    const restTweets = tweets.filter((tweet, idx) => idx !== deleteIndex);
    setTweets(restTweets);
  };
  const tweetsRenderer = (tweet, idx) => {
    return (
      <Tweet
        key={tweet.id}
        tweet={tweet}
        handleDeleteTweet={handleDeleteTweet}
        idx={idx}
      />
    );
  }

 

tweet.js
// tweet.js

const Tweet = ({ tweet, handleDeleteTweet, idx }) => { // 함수 전달
.
.
.
// 적당한 곳에 넣어준다.

   <button
    className="tweet__deleteButton"
    onClick={() => handleDeleteTweet(tweet.username, idx)}>
   <i className="far fa-trash-alt"></i>
   </button>
728x90
⬆︎