Language/React

[React] 예시 9. 이전 작업 node.js 환경 세팅

print(blue) 2024. 6. 4. 09:02

https://print-blue.tistory.com/201

 

코드 옮기기 

 

node.js 환경에서는 자동으로 react 를 가져올 수 없어서 import 해줘야 함 

import React from 'react';

 

이미지 폴더가 없어서 이미지가 깨지는데 public 폴더에 복사 !

css 추가 

# p240603\first-my-app\src\index.css

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}
/* 스타일 적용 */
body {
  text-align: center;
}
form {
  margin-bottom: 20px;
}
.main-card button {
  position: relative;
  left: -45px;
  bottom: 15px;
}
.favorites {
  list-style: none;
  display: flex;
  gap: 15px;
  justify-content: center;
  flex-wrap: wrap;
}
.favorites img {
  width: 150px;
}

 

컴포넌트 분리

src 폴더에 components 폴더 생성

# p240603\first-my-app\src\App.js

import logo from './logo.svg';
import React from 'react';
import './App.css';

import H1 from './components/H1';
import Form from './components/Form';
import MainCard from './components/MainCard';
import Favorites from './components/Favorites';

const jsonLocalStorage = {
  setItem: (key, value) => {
    localStorage.setItem(key, JSON.stringify(value));
  },
  getItem: (key) => {
    return JSON.parse(localStorage.getItem(key));
  },
}

/* component : 상태 끌어올리기(lifting state up) */
const App = () => {
  const foodOne = "img/food-one.jpg";
  const foodTwo = "img/food-two.jpg";
  const foodTree = "img/food-three.jpg";

  // 보여지는 이미지 상태값
  const [favorites, setFavorites] = React.useState(() => {
    // 초기값 빈 배열로 설정
    return jsonLocalStorage.getItem('favorites') || [];
  });
  // 페이지수 상태값
  const [counter, setCounter] = React.useState(() => {
    return jsonLocalStorage.getItem('counter');
  }
  );
  // 좋아요 버튼 상태값
  const [heartCounter, setHeartCounter] = React.useState(0);

  function updateCounter() {
    setCounter((pre) => {
      const nextCounter = pre + 1;
      jsonLocalStorage.setItem('counter', nextCounter);
      return nextCounter;
    });
  }

  function handlerHeartClick() {
    setHeartCounter(heartCounter + 1);

    setFavorites((pre) => {
      const nextFavorites = [...pre, foodTree];
      jsonLocalStorage.setItem('favorites', nextFavorites);
      return nextFavorites;
    });
  }

  return (
    <div>
      <H1>{counter}</H1>
      <Form updateCounter={updateCounter} />
      <MainCard
        img="img/food-one.jpg"
        handlerHeartClick={handlerHeartClick}
        heartCounter={heartCounter}
      />
      <Favorites favorites={favorites} />
    </div>
  );
}

export default App;
# p240603\first-my-app\src\components\Favorites.js

import FoodItem from "./FoodItem";

/* component */
const Favorites = ({ favorites }) => {
  return (
    <ul className="favorites">
      {favorites.map((food, index) => (<FoodItem img={food} key={index} />))}
    </ul>
  );
}

export default Favorites;
# p240603\first-my-app\src\components\FoodItem.js

/* component */
const FoodItem = ({ img }) => {
  return (
    <li>
      <img
        src={img}
        alt="음식"
        style={{ width: '150px', height: '100px', backgroundSize: 'contain' }}
      />
    </li>
  );
}

export default FoodItem;
# p240603\first-my-app\src\components\Form.js

import React from 'react';

/* component */
const Form = ({ updateCounter }) => {
  const [value, setValue] = React.useState('');
  const [errorMsg, setErrorMsg] = React.useState('');

  const hangul = (text) => /[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/.test(text);

  function handlerInputChange(data) {
    const userValue = data.target.value;
    setValue(userValue.toUpperCase());

    if (hangul(userValue)) {
      setErrorMsg('한글은 입력할 수 없습니다.');
    } else {
      setErrorMsg('');
    }
  }

  function handlerFormSubmit(event) {
    event.preventDefault();
    if (value === '') {
      setErrorMsg('빈 값은 추가 할 수 없습니다.');
      return;
    }
    updateCounter();
  }

  return (
    <form action="" onSubmit={handlerFormSubmit}>
      <input
        type="text"
        name="name"
        placeholder="상품명을 입력하세요"
        onChange={handlerInputChange}
        value={value}
      />
      <button type="submit">추가</button>
      <p style={{ color: '#f00' }}>{errorMsg}</p>
    </form>
  );
}

export default Form;
# p240603\first-my-app\src\components\H1.js

/* component */
const H1 = (props) => {
  return <h1>상품 {props.children} 페이지</h1>
}

export default H1;
# p240603\first-my-app\src\components\MainCard.js

/* component */
const MainCard = ({ img, handlerHeartClick, heartCounter }) => {
  return (
    <div className="main-card">
      <img
        src={img}
        alt="올리브 오일"
        width="400"
      />
      <button onClick={handlerHeartClick}>
        🤍{heartCounter > 0 && heartCounter}
      </button>
    </div>
  );
}

export default MainCard;