2020.코딩일지

React실습 - react-twittler-state-props 본문

JS & React

React실습 - react-twittler-state-props

개발하는라푼젤 2022. 7. 17. 01:52
728x90

    Sprint - React  Twittler state-props           

깃헙에서 fork - 내 로컬로 clone

npm install

.json을 보니, 응? 이미 "react-router-dom": "^6.3.0", 설치되어있습니다?!

( 생략 )npm install react-router-dom

(npm t) npm run test

npm run start

npm run submit

 

 

myPage.js Tweets.js

 

Tweets안에 Tweet컴포넌트를 넣는다.

App.js
import React from 'react';
// TODO : React Router DOM을 설치 후, import 구문을 이용하여 BrowserRouter, Routes, Route 컴포넌트를 불러옵니다.
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Sidebar from './Sidebar';
import Tweets from './Pages/Tweets';
// TODO : MyPage, About 컴포넌트를 import 합니다.
import Mypage from './Pages/MyPage';
import About from './Pages/About'
import './App.css';

const App = (props) => {
  return (
    <BrowserRouter>
      <div className="App">
        <main>
          <Sidebar />
          <section className="features">
            <Routes>
              <Route path="/" element={<Tweets />} />
              <Route path="/about" element={<About />} />
              <Route path="/mypage" element={<Mypage />} />
            </Routes>
            {/* TODO : 유어클래스를 참고해서, 테스트 케이스를 통과하세요.
            TODO : React Router DOM 설치 후 BrowserRouter, Routes, Route의 주석을 해제하고 Routes, Route 컴포넌트를 적절하게 작성합니다. */}
            {/* Route 예시: <Route path="/" element={<Tweets />}></Route> */}
          </section>
        </main>
      </div>
    </BrowserRouter>
  );
};

// ! 아래 코드는 수정하지 않습니다.
export default App;​
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

import dummyTweets from './static/dummyData';

ReactDOM.render(
  <App dummyTweets={dummyTweets} />,
  document.getElementById('root')
);​
Sidebar.js
import React from 'react';
// TODO : React Router DOM의 Link 컴포넌트를 import 합니다.
import { Link } from 'react-router-dom';

const Sidebar = () => {
  return (
    <section className="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>
    </section>
  );
};

export default Sidebar;​
Footer.js
import React from 'react';

const Footer = () => {
  return <div>
    <footer> 
      copyright from EG220718
    </footer>
  </div>;
};
// TODO : Footer 함수 컴포넌트를 작성합니다. 시멘틱 요소 footer가 포함되어야 합니다.
// 🥺대문자 쓰면 무한루프 돌면서 heap out of memory 에러 발생
// 아마도 'Footer가 Footer를 그러니까 Footer가 Footer를 아니그래서 Footer가 Footer를 그랬다니까'무한반복st.이여서 그러지 않았나 생각해봅니🙃

export default Footer;​
pages/Tweets.js -- ver1.페어님것참고
// 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 [name, setName] = useState('');
  const [message, setMessage] = useState('');
  const [list, setList] = useState([]);
const handleButtonClick = (event) => {
    const tweet = {
      id: '0',
      username: name,
      picture: 'https://randomuser.me/api/portraits/men/98.jpg',
      content: message,
      createdAt: new Date(),
      updatedAt: new Date()
    };
    // TODO : Tweet button 엘리먼트 클릭시 작동하는 함수를 완성하세요.
    // 트윗 전송이 가능하게 작성해야 합니다.
    setList([tweet, ...list]);

  };

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

  const handleChangeMsg = (event) => {
    // TODO : Tweet textarea 엘리먼트에 입력 시 작동하는 함수를 완성하세요.
    setMessage(event);
  };
return (
    <React.Fragment>
      <div className="tweetFormcontainer">
        <div className="tweetFormwrapper">
          <div className="tweetFormprofile">
            <img src="https://randomuser.me/api/portraits/men/98.jpg" />
          </div>
          <div className="tweetForminputContainer">
            <div className="tweetForminputWrapper">
              <div className="tweetForminput">
                <input onChange={(e)=>handleChangeUser(e.target.value)}
                  type="text"
                  defaultValue="parkhacker"
                  placeholder="your username here.."
                  className="tweetForm__input--username"
                ></input>
                <textarea className="tweetForm__input--message" 
                onChange={(e)=>handleChangeMsg(e.target.value)}></textarea>
              </div>
              <div className="tweetFormcount" role="status">
                <span className="tweetFormcounttext">
                  {/* TODO : 트윗 총 개수를 보여줄 수 있는 Counter를 작성하세요. */}
                  {`total: ${dummyTweets.length}`}
                </span>
              </div>
            </div>
            <div className="tweetFormsubmit">
            <button className="tweetForm__submitButton" onClick={()=>handleButtonClick()}>
              <div className="tweetFormsubmitIcon">Tweet</div>
            </button>
            </div>
          </div>
        </div>
      </div>
      <div className="tweet__selectUser"></div>
      <ul className="tweets">
        {list.map((data, index) =>(
          <Tweet key={index} tweet={data}/>
        ))}

        {dummyTweets.map((data, index)=>(
          <Tweet key={index} tweet={data} />
        ))}
      </ul>
      <Footer />
    </React.Fragment>
  );
};

export default Tweets;​
pages/Tweets.js --ver2.선배님것참고
// 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 getRandomNumber = (min, max) => {
  return parseInt(Math.random() * (Number(max) - Number(min) + 2));
};

const [user, setUser] = useState('parkhacker');
const [msg, setMsg] = useState('');
const [tweets, setTweets] = useState(dummyTweets);

  const handleButtonClick = (event) => {
    const tweet = {
      id: dummyTweets.length+1,
      username: user,
      picture: `https://randomuser.me/api/portraits/women/${getRandomNumber(
        1,
        98
      )}.jpg`,
      content :msg,
      createdAt : new Date().toLocaleDateString('ko-kr'),
      updatedAt : new Date().toLocaleDateString('ko-kr'),
    };
    // TODO : Tweet button 엘리먼트 클릭시 작동하는 함수를 완성하세요.
    // 트윗 전송이 가능하게 작성해야 합니다.
    setTweets([tweet, ...tweets])
  };

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

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

  return (
    <React.Fragment>
      {/* Fragment는 DOM에 별도의 노드를 추가하지 않고 여러 자식을 그룹화 할 수 있다. https://ko.reactjs.org/docs/fragments.html */}
      <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"
                  value={user} //여기추가
                  placeholder="your username here.."
                  className="tweetForm__input--username"
                  onChange={handleChangeUser}
                ></input>
                {/* TODO : 트윗을 작성할 수 있는 textarea 엘리먼트를 작성하세요. */}
                <textarea

                defaultValue={""}
                placeholder="your message here.."
                className="tweetForm__input--message"
                onChange={handleChangeMsg}
                value={msg}

              ></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>
              {/* TODO : 작성한 트윗을 전송할 수 있는 button 엘리먼트를 작성하세요. */}
              <button className="tweetForm__submitButton" onClick={handleButtonClick}>Tweet</button>
            </div>
          </div>
        </div>
      </div>

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

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

export default Tweets;​
pages/MyPage.js
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의 트윗만 보여줘야 합니다.
  const filteredTweets = dummyTweets.filter((tweet) => {
    return tweet.username === 'parkhacker';
  })

  return (
    <section className="myInfo">
      <div className="myInfo__container">
        <div className="myInfo__wrapper">
          <div className="myInfo__profile">
            <img src={filteredTweets[0].picture} /> 
            {/*[1]을 [0]으로 수정 */}
          </div>
          <div className="myInfo__detail">
            <p className="myInfo__detailName">
              {filteredTweets[0].username} Profile
              {/*[1]을 [0]으로 수정 */}
            </p>
            <p>28 팔로워 100 팔로잉</p>
          </div>
        </div>
      </div>
      <ul className="tweets__mypage">
        <Tweet tweet={filteredTweets[0]}/>
        {/* TODO : 주어진 트윗 목록(dummyTweets)중 현재 유져인 parkhacker의 트윗만 보여줘야 합니다. */}
        {/* 또는 아래처럼. parkhacker의 tweet갯수가 0개이상일 때, map함수가 실행되게끔 할수도 있다.  */}
        {/* {filteredTweets.length > 0 && filteredTweets.map((filteredTweet, index) => <Tweet key={index} tweet={filteredTweet} setTweet={props.setLoadedTweets} allTweet={props.loadedTweets}/>)} */}
        
      </ul>
      <Footer />
    </section>
  );
};

export default MyPage;​
pages/About.js (bare minimum이라 수정한건 없음)
import React from 'react';
import Footer from '../Footer';
import './About.css';

const About = (props) => {
  return (
    <section className="aboutTwittler">
      <div className="aboutTwittler__container">
        <div className="aboutTwittler__wrapper">
          <div className="aboutTwittler__detail">
            <p className="aboutTwittler__detailName">React Twittler Info</p>
          </div>
        </div>
      </div>
      <div className="aboutTwittler__content">
        <i className="fas fa-users"></i>
        <p>나만의 Twittler 소개페이지를 꾸며보세요.</p>
      </div>
      <Footer />
    </section>
  );
};

export default About;​
components/Tweet.js
import React from 'react';
import './Tweet.css';

const Tweet = ({ tweet }) => {
  const parsedDate = new Date(tweet.createdAt).toLocaleDateString('ko-kr');
//  const parsedDate = new Date(tweet.createdAt).toLocaleDateString(); //이것도가능

  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">
            {/* TODO : 유져 이름이 있어야 합니다. */}
            {/* TODO : 트윗 생성 일자가 있어야 합니다. parsedDate를 이용하세요. */}
            <span className="tweet__username">{tweet.username}</span>
            <span className="tweet__createdAt">{parsedDate}</span>
          </div>
        </div>
        <div className="tweet__message">
          {/* TODO : 트윗 메세지가 있어야 합니다. */}
          {tweet.content}
        </div>
      </div>
    </li>
  );
};

export default Tweet;​

 

Comments