react state&props 과제

안윤경·2022년 8월 3일

과제

목록 보기
14/20

Bare Minimum Requirement

1. Sidebar.js Sidebar 컴포넌트

     {/* TODO : Link 컴포넌트를 작성하고, to 속성을 이용하여 경로(path)를 연결합니다. */}
      <Link to ="/"><i className="far fa-comment-dots"></i></Link>
      <Link to ="/about"><i className="far fa-question-circle"></i></Link>
      <Link to ="/mypage"><i className="far fa-user"></i></Link>

Tweet.js Tweet 컴포넌트

import React from 'react';
import './Tweet.css';

const Tweet = ({ tweet }) => {
  const parsedDate = new Date(tweet.createdAt).toLocaleDateString('ko-kr');

  return (
    <li className="tweet" id={tweet.id} >
      
      <div className="tweet__profile">
        

        <img src={tweet.picture} />
      </div>
      <div className="tweet__content">
        <div className="tweet__userInfo" >
        
          <div className="tweet__userInfo--wrapper">
            <span className="tweet__username">{tweet.username}</span>
            {/* TODO : 유져 이름이 있어야 합니다. */}
            {/* TODO : 트윗 생성 일자가 있어야 합니다. parsedDate를 이용하세요. */}
            <span className='tweet__createdAt'>{parsedDate}</span>
            
          </div>
        </div>
        <div className="tweet__message">
          {tweet.content}
        </div>
      </div>
    </li>
  );
};

export default Tweet;

MyPage.js MyPage 컴포넌트

import React from 'react';
import Footer from '../Footer';
import Tweet from '../Components/Tweet';
import './MyPage.css';
import dummyTweets from '../static/dummyData';

const MyPage = () => {
  const filteredTweets = dummyTweets;
  // TODO : 주어진 트윗 목록(dummyTweets)중 현재 유져인 parkhacker의 트윗만 보여줘야 합니다.
  


  return (
    <section className="myInfo">
      <div className="myInfo__container">
        <div className="myInfo__wrapper">
          <div className="myInfo__profile">
            <img src={filteredTweets[1].picture} />
          </div>
          <div className="myInfo__detail">
            <p className="myInfo__detailName">
              {filteredTweets[1].username} Profile
            </p>
            <p>28 팔로워 100 팔로잉</p>
          </div>
        </div>
      </div>
      <ul className="tweets__mypage">
        <Tweet tweet={filteredTweets[1]}/>        
      </ul>
      <Footer />
    </section>
  );
};

export default MyPage;
  • 여기서 매우매우 헷갈렸음
    -> why? props를 배웠음에도 이용을 못해서 조건 중에 -- ✓ MyPage 컴포넌트의 자식인 Tweet 컴포넌트에 props로 각 트윗의 정보(dummyTweets의 요소)가 전달되어야 합니다. (2 ms)--라는 조건이 잇음에도 불구하고 전처럼 하나하나 적으려고 함
    여기서 mypage는 부모컴포넌트가 되고 tweet는 자식 컴포넌트가 된다 즉 부모컴포넌트에 있는 걸 props로 자식 컴포넌트에 전달!할 수 있음을 기억하자

    < Tweet tweet={filteredTweets[1]}/> 맨처음 Tweet는 자식컴포넌트의 이름이고 tweet는 props이다!

    또한 여기서 --주어진 트윗 목록(dummyTweets) 중 현재 유저인 parkhacker의 트윗만 보여야 합니다. (4 ms)-- 중 filter를 써서 하려고 했는데 이미 tweet.js안에 [1]인 것만 리턴을 해서 안해준듯 하다 !!!!레퍼런스를 보니

    const filteredTweets = dummyTweets.filter(tweet => tweet.username === 'parkhacker');

    로 해도 실행이 되는 걸 볼 수있었다.

    {filteredTweets.map(tweet => {
    return < Tweet tweet={tweet} key={tweet.id}>
    })}

내가 쓴 것과 달리 map을 써서 해당되는 id값을 보내준 것 같다 아마 위에는 필터로 했으나 밑에서 막혀 실행이 안됬던 거로 보인다
중요한점은 key값이 있어야 고유값이 있어 돌릴 수 있는 것 같다.


Tweets.js Tweets 컴포넌트

코드를 입력하세요// TODO : useState를 react로 부터 import 합니다.
import React, { useState } from 'react';
import Footer from '../Footer';
import Tweet from '../Components/Tweet';
import './Tweets.css';
import dummyTweets from '../static/dummyData';

const Tweets = () => {
  // TODO : 새로 트윗을 작성하고 전송할 수 있게 useState를 적절히 활용하세요.
  const[newTweet , setnewTweet] = useState(""); //이름을 넣을때 사용인데 좀 더 명확하게 바꾸기
  const[newcontent, setnewContent] = useState("");  //내용
  const[tweets,setTweet] =useState(dummyTweets);//데이터
  const[opt ,setOpt] = useState(options)
  const[clickedName,setClickedName] = useState("")
  const [filteredUser, setFilteredUser] =useState(tweets);
  
   
  // const getRandomNumber = (min, max) => {
  //   return parseInt(Math.random() * (Number(max) - Number(min) + 2));
  // };
 
  const options = [{value:"kimcoding",label:"kimcoding"},
                {value:"parkhacker",label:"parkhacker"},
                {value:"leedesign",label:"leedesign"},
                {value:"songfront",label:"songfront"},
                {value:"choiback",label:"choiback"}];

      
                const handleDeleteClick = (event) => {
                setTweet(tweets.filter((el) => {  return el.id !== Number(event.target.attributes.id.value);}))}
                

  const handleSelectClick = (event) => {
    setClickedName(event.target.value)
    setFilteredUser(tweets.filter((el)=>{return el.username === event.target.value;}))
  };

  const handleButtonClick = (event) => {
   //버튼을 누르면 새로운 내용이 추가되어야 하는 것 이건 dom의 이벤트핸들러와 비슷하다
    const tweet = {
      id : tweets.length+1,(더미데이터의 갯수 +1)
      username: newTweet,
      picture:`https://randomuser.me/api/portraits/men/98.jpg`, 랜덤이미지?
      content:newcontent,
      createdAt: new Date(),//현재시간
      updateAt: new Date(),
    };
      setTweet([tweet,...tweets])//지금 추가한 내용 + 원래더미데이터의 내용
    };
   
  
  <button onClick={handleButtonClick}>Tweets</button>
    

  const handleChangeUser = (event) => {
    // TODO : Tweet input 엘리먼트에 입력 시 작동하는 함수를 완성하세요.
    setnewTweet(event.target.value)
  };

  const handleChangeMsg = (event) => {
    
    // TODO : Tweet textarea 엘리먼트에 입력 시 작동하는 함수를 완성하세요.
    setnewContent(event.target.value)
  };

  return (
    <React.Fragment>
      <div className="tweetForm__container">
        <div className="tweetForm__wrapper">
          <div className="tweetForm__profile">
            <img src="https://randomuser.me/api/portraits/men/98.jpg" />
          </div>
          <div className="tweetForm__inputContainer">
            <div className="tweetForm__inputWrapper">
              <div className="tweetForm__input">
              
                <input
                  type="text"
                  defaultValue="parkhacker"
                  placeholder="your username here.."
                  className="tweetForm__input--username"
                  onChange={handleChangeUser}
                ></input>
                {/* TODO : 트윗을 작성할 수 있는 textarea 엘리먼트를 작성하세요. */}
                <textarea
                  type ="text"
                  className="tweetForm__input--message"
                  onChange={handleChangeMsg}
                  value={newcontent}>

                  </textarea>
              </div>
              <div className="tweetForm__count" role="status">
                <span className="tweetForm__count__text">
                  {/* TODO : 트윗 총 개수를 보여줄 수 있는 Counter를 작성하세요. */}
                  {'total: ' + tweets.length}
                </span>
              </div>
            </div>
            <div className="tweetForm__submit">
              <div className="tweetForm__submitIcon"></div>
              <button className = "tweetForm__submitButton" onClick={handleButtonClick}>Tweet</button>
              {/* TODO : 작성한 트윗을 전송할 수 있는 button 엘리먼트를 작성하세요. */}
      
            </div>
         
          </div>
        </div>
      </div>
      
      <div className="tweet__selectUser">

            <select
            newTweet = "users" id = "user_select"
            onChange = {handleSelectClick}>
            <option value = "">-- click to filter tweets by username --</option>
            {opt.map((el) => {return <option value = {el.value} key = {el.value}>{el.label}</option> })}
       
            </select>
            </div>
            {clickedName === "" ? tweets.map((el) => {return <Tweet tweet={ el } key = {el.id} handleDeleteClick = {handleDeleteClick} /> })
            : filteredUser.map((el) => {return <Tweet tweet={ el } key = {el.id} />})

            }
  
      <ul className="tweets">
        {/* TODO : 하나의 트윗이 아니라, 주어진 트윗 목록(dummyTweets) 갯수에 맞게 보여줘야 합니다. */}
       
      // TODO : useState를 react로 부터 import 합니다.
import React, { useState } from 'react';
import Footer from '../Footer';
import Tweet from '../Components/Tweet';
import './Tweets.css';
import dummyTweets from '../static/dummyData';

const Tweets = () => {
  // TODO : 새로 트윗을 작성하고 전송할 수 있게 useState를 적절히 활용하세요.
  const[newTweet , setnewTweet] = useState(""); //애매
  const[newcontent, setnewContent] = useState("");  
  const[tweets,setTweet] =useState(dummyTweets);
  const[opt ,setOpt] = useState(options)
  const[clickedName,setClickedName] = useState("")
  const [filteredUser, setFilteredUser] =useState(tweets);
  
   
  // const getRandomNumber = (min, max) => {
  //   return parseInt(Math.random() * (Number(max) - Number(min) + 2));
  // };
 
  const options = [{value:"kimcoding",label:"kimcoding"},
                {value:"parkhacker",label:"parkhacker"},
                {value:"leedesign",label:"leedesign"},
                {value:"songfront",label:"songfront"},
                {value:"choiback",label:"choiback"}];

      
                const handleDeleteClick = (event) => {
                setTweet(tweets.filter((el) => {  return el.id !== Number(event.target.attributes.id.value);}))}
                

  const handleSelectClick = (event) => {
    setClickedName(event.target.value)
    setFilteredUser(tweets.filter((el)=>{return el.username === event.target.value;}))
  };

  const handleButtonClick = (event) => {
   
    const tweet = {
      id : tweets.length+1,
      username: newTweet,
      picture:`https://randomuser.me/api/portraits/men/98.jpg`,
      content:newcontent,
      createdAt: new Date(),
      updateAt: new Date(),
    };
      setTweet([tweet,...tweets])
    };
   
  
  <button onClick={handleButtonClick}>Tweets</button>
    

  const handleChangeUser = (event) => {
    // TODO : Tweet input 엘리먼트에 입력 시 작동하는 함수를 완성하세요.
    setnewTweet(event.target.value)
  };

  const handleChangeMsg = (event) => {
    
    // TODO : Tweet textarea 엘리먼트에 입력 시 작동하는 함수를 완성하세요.
    setnewContent(event.target.value)
  };

  return (
    <React.Fragment>
      <div className="tweetForm__container">
        <div className="tweetForm__wrapper">
          <div className="tweetForm__profile">
            <img src="https://randomuser.me/api/portraits/men/98.jpg" />
          </div>
          <div className="tweetForm__inputContainer">
            <div className="tweetForm__inputWrapper">
              <div className="tweetForm__input">
              
                <input
                  type="text"
                  defaultValue="parkhacker"
                  placeholder="your username here.."
                  className="tweetForm__input--username"
                  onChange={handleChangeUser}
                ></input>
                {/* TODO : 트윗을 작성할 수 있는 textarea 엘리먼트를 작성하세요. */}
                <textarea
                  type ="text"
                  className="tweetForm__input--message"
                  onChange={handleChangeMsg}
                  value={newcontent}>

                  </textarea>
              </div>
              <div className="tweetForm__count" role="status">
                <span className="tweetForm__count__text">
                  {/* TODO : 트윗 총 개수를 보여줄 수 있는 Counter를 작성하세요. */}
                  {'total: ' + tweets.length}
                </span>
              </div>
            </div>
            <div className="tweetForm__submit">
              <div className="tweetForm__submitIcon"></div>
              <button className = "tweetForm__submitButton" onClick={handleButtonClick}>Tweet</button>
              {/* TODO : 작성한 트윗을 전송할 수 있는 button 엘리먼트를 작성하세요. */}
      
            </div>
         
          </div>
        </div>
      </div>
      
      <div className="tweet__selectUser">

            <select
            newTweet = "users" id = "user_select"
            onChange = {handleSelectClick}>
            <option value = "">-- click to filter tweets by username --</option>
            {opt.map((el) => {return <option value = {el.value} key = {el.value}>{el.label}</option> })}
       
            </select>
            </div>
            {clickedName === "" ? tweets.map((el) => {return <Tweet tweet={ el } key = {el.id} handleDeleteClick = {handleDeleteClick} /> })
            : filteredUser.map((el) => {return <Tweet tweet={ el } key = {el.id} />})

            }
  
      <ul className="tweets">
        {/* TODO : 하나의 트윗이 아니라, 주어진 트윗 목록(dummyTweets) 갯수에 맞게 보여줘야 합니다. */}
       
       {tweets.map((tweet) => <Tweet tweet={tweet}/> )}
       
      </ul>
      <Footer />
    </React.Fragment>
  );
  }

export default Tweets;

       
      </ul>
      <Footer />
    </React.Fragment>
  );
  }

export default Tweets;

힘들었던 곳 --✓ 하나의 트윗이 아니라, 주어진 트윗(dummyTweets) 개수에 맞게 보여줘야 합니다. (50 ms)--

{tweets.map((tweet) => < Tweet tweet={tweet}/> )}

map을 쓰는 것 까지는 이해를 했으나 앞에 dummydate tweets로 선언해주었는데 그대로 dummydata를 써서 안된듯 하다

데이터를 추가하는 handleButtonClick이벤트에서 마지막에 데이터를 앞에 넣을때

    // tweets.unshift(tweet);
    // setTweets(tweets);

일때 문제가 생긴다 왜냐 얕은 복사라 1번째의 tweets와 두번째의 tweets는 같은 주소라 리렌더링을 하지않기 때문이다!

중요한점!
이번 과제의 핵심입니다. Tweets 컴포넌트에서 어떤 데이터가 state가 되어야 하고, 어떤 데이터를 props로 하위 컴포넌트에 전달해야 할지 고민하고 아래 기술 요구사항을 구현합니다.

state 인것

  • 크게보면 트윗을 전송하는 form이라고 할 수 있다
  • 이름을 넣을 수 있는 input 창과 input의 값이 변경될 때 onchange이벤트 핸들러
  • 글을 작성하는 textarea와 textarea의 값이 변경될떼 onchange이벤트 핸들러
  • button을 눌렀을 때 트윗을 전송하는 onchange핸들러
  • 새로운 트윗이 추가되는 부분
profile
프론트엔드 개발자 안윤경입니다

0개의 댓글