https://print-blue.tistory.com/191
[React] 예시 1. 순수한 자바스크립트 페이지를 리액트 JSX 문법으로 바꾸기
1. 자바스크립트로 구현# p240530/react/ex01-javascript.html 상품 1 페이지 추가 🤍 기본 페이지기능 1. 좋아요 버튼 클릭시 하트 색 변경&이미지 추가된 모습 2. 자바스크립트를 리액트 JSX 문법으로 변
print-blue.tistory.com
https://print-blue.tistory.com/193
[React] 예시 2. 순수한 자바스크립트 페이지를 리액트 component 로 바꾸기
https://print-blue.tistory.com/191 [JavaScript ] 예시 1. 순수한 자바스크립트 페이지# p240530/react/ex01-javascript.html 상품 1 페이지 추가 🤍 기본 페이지기능 1. 좋아요 버튼 클릭시 하트 색 변경&이미지 추가
print-blue.tistory.com
https://print-blue.tistory.com/194
[React] 예시 3. 순수한 자바스크립트 페이지를 리액트 component 로 바꾸고, 기능 추가(이벤트 설정)
https://print-blue.tistory.com/191 [JavaScript ] 예시 1. 순수한 자바스크립트 페이지# p240530/react/ex01-javascript.html 상품 1 페이지 추가 🤍 기본 페이지기능 1. 좋아요 버튼 클릭시 하트 색 변경&이미지 추가
print-blue.tistory.com
https://print-blue.tistory.com/195
[React] 예시 4. 순수한 자바스크립트 페이지를 리액트 component 로 바꾸고, 기능 추가하기(상태 설정
https://print-blue.tistory.com/191 [JavaScript ] 예시 1. 순수한 자바스크립트 페이지# p240530/react/ex01-javascript.html 상품 1 페이지 추가 🤍 기본 페이지기능 1. 좋아요 버튼 클릭시 하트 색 변경&이미지 추가
print-blue.tistory.com
https://print-blue.tistory.com/198
[React] 예시 5. 순수한 자바스크립트 페이지를 리액트 component 로 바꾸고, 기능 추가 후 유효성 검
https://print-blue.tistory.com/191 [React] 예시 1. 순수한 자바스크립트 페이지를 리액트 JSX 문법으로 바꾸기1. 자바스크립트로 구현# p240530/react/ex01-javascript.html 상품 1 페이지 추가 🤍 기본 페이지기능
print-blue.tistory.com
https://print-blue.tistory.com/200
[React] 예시 5. 순수한 자바스크립트 페이지를 리액트 component 로 바꾸고, 기능 추가 와 유효성 검
https://print-blue.tistory.com/191 [React] 예시 1. 순수한 자바스크립트 페이지를 리액트 JSX 문법으로 바꾸기1. 자바스크립트로 구현# p240530/react/ex01-javascript.html 상품 1 페이지 추가 🤍 기본 페이지기능
print-blue.tistory.com
다시 정리하면서 잘 되는지 확인해보다가 짚고 넘어가야할 문제가 있음 !
App
const jsonLocalStorage = {
setItem : (key, value) => {
console.log('[jsonLocalStorage] setItem 실행 !');
localStorage.setItem(key, JSON.stringify(value));
},
getItem : (key) => {
console.log('[jsonLocalStorage] getItem 실행 !');
return JSON.parse(localStorage.getItem(key));
},
}
호출을 안했는데 왱 ?
그 이유는 사용자가 접속을 하게 되면 페이지를 요청하게 되는데,
파일이 로드를 하는데 그럼 웹 브라우저가 렌더링하는데 코드를 다 읽으면서 코드 실행을 하는데
react 코드도 실행 됨 !
App 이 실행되면서 메모리 할당될 때, ReactDOM 에서 호출하면서 실행 됐기 때문에
ReactDOM.createRoot(foodLiInsert).render(<App />);
getItem 이 2번 호출이 됐음 !
const [favorites, setFavorites] = React.useState(jsonLocalStorage.getItem('favorites') || [foodOne, foodTwo]);
const [counter, setCounter] = React.useState(jsonLocalStorage.getItem('counter'));
그렇다면 아래 사진 처럼 setItem 호출되고 또 getItem 이 2번 출력됐을까
그 이유는 리액트 특징에 있다.
우리가 입력창에 무언가를 입력할 때 웹 페이지에 변경이 되는데,(이벤트 발생시켰다 함)
그럴 때 리액트는 다시 렌더링 함 ! 그래서 또 다시 렌더링이 됐기 때문에 2번 출력이 됨
이러한 현상을 리렌더링 ! 새로고침 한 거 아님 ! 현 페이지에서 렌더링 된거임
그럼 이런 방식보다는 해당하는 인수를 익명 함수로 넘겨주고, 렌더링될 때 읽고, 리렌더링일 땐 안읽는다
즉 익명함수로 한 이유는 리렌더링이 될 때 다시 호출하지 않음 !
리렌더링할 때 초기화 작업을 하지 않는다 !
// const [favorites, setFavorites] = React.useState(jsonLocalStorage.getItem('favorites') || [foodOne, foodTwo]);
const [favorites, setFavorites] = React.useState(() => {
console.log('favorites useState 실행됨');
return jsonLocalStorage.getItem('favorites') || [foodOne, foodTwo];
}
);
// const [counter, setCounter] = React.useState(jsonLocalStorage.getItem('counter'));
const [counter, setCounter] = React.useState(() => {
console.log('counter useState 실행됨');
return jsonLocalStorage.getItem('counter');
}
);
이 작업은 상태값과 관련이 있기 때문에 updateCounter / handlerHeartClick를 다시 볼건데
상태변경을 수정해야함 ㅠ
setCounter() 와 setFavorites() 를 호출할 때 인수로 들어오는 값이 return 값이기 때문에 return 을 해줘야 함
증가된 값을 jsonLocalStorage 에서도 사용해야 됨 !
콜백 함수 형태로 만들어줄거임
꼭 이 작업을 해야되는 건 아니지만, 이런 방법도 있다 ~ !
function updateCounter() {
// [변경 전] ======================
// const nextCounter = counter + 1;
// setCounter(nextCounter);
// jsonLocalStorage.setItem('counter', nextCounter);
// [변경 후] ======================
// setCounter() 인수로 들어온 값이 return 값이기 때문에 return 을 해줘야 함
// 정리 : return 값이 setCounter() 인수로 들어옴
// 증가된 값을 jsonLocalStorage 에도 사용해야 함
// 콜백 함수로 만들어준거임
setCounter((pre) => {
// 1 증가된 값 나오도록
const nextCounter = pre + 1;
jsonLocalStorage.setItem('counter', nextCounter);
return nextCounter;
});
}
function handlerHeartClick() {
// setFavorites([...favorites, foodTree]);
// jsonLocalStorage.setItem('favorites', [...favorites, foodTree]);
// [변경 전] ======================
// const nextFavorites = [...favorites, foodTree];
// setFavorites(nextFavorites);
// jsonLocalStorage.setItem('favorites', nextFavorites);
// [변경 후] ======================
// 상태값 변경이라 불림
setFavorites((pre) => {
const nextFavorites = [...pre, foodTree];
jsonLocalStorage.setItem('favorites', nextFavorites);
return nextFavorites;
});
}
전체 코드
<script type="text/babel">
const jsonLocalStorage = {
setItem : (key, value) => {
console.log('[jsonLocalStorage] setItem 실행 !', key);
localStorage.setItem(key, JSON.stringify(value));
},
getItem : (key) => {
console.log('[jsonLocalStorage] getItem 실행 !', key);
return JSON.parse(localStorage.getItem(key));
},
}
/* component */
const H1 = (props) => {
return <h1>상품 {props.children} 페이지</h1>
}
/* 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>
);
}
/* component */
const MainCard = ({ img, handlerHeartClick }) => {
return (
<div className="main-card">
<img
src={img}
alt="올리브 오일"
width="400"
/>
<button onClick={handlerHeartClick}>🤍</button>
</div>
);
}
/* component */
const FoodItem = ({ img }) => {
return (
<li>
<img
src={img}
alt="음식"
style={{ width: '150px', height: '100px', backgroundSize: 'contain' }}
/>
</li>
);
}
/* component */
const Favorites = ({ favorites }) => {
return (
<ul className="favorites">
{favorites.map(food => (<FoodItem img={food} key={food} />))}
</ul>
);
}
/* 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') || [foodOne, foodTwo];
});
const [counter, setCounter] = React.useState(() => {
return jsonLocalStorage.getItem('counter');
}
);
function updateCounter() {
setCounter((pre) => {
const nextCounter = pre + 1;
jsonLocalStorage.setItem('counter', nextCounter);
return nextCounter;
});
}
function handlerHeartClick() {
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}
/>
<Favorites favorites={favorites} />
</div>
);
}
const foodLiInsert = document.querySelector('#food-li-insert');
ReactDOM.createRoot(foodLiInsert).render(<App />);
</script>