import React, { useState } from "react"; import "./styles.css"; function CheckboxExample() { const [isChecked, setIsChecked] = useState(false); const handleChecked = (event) => { setIsChecked(event.target.checked); }; return ( <div className="App"> <input type="checkbox" checked={isChecked} onChange={handleChecked} /> <span>{isChecked ? "Checked!!" : "Unchecked"}</span> </div> ); } export default CheckboxExample;
import { useState } from "react";
const [state 저장 변수, state 갱신 함수] = useState(상태 초기 값); state의 초깃값 const [isChecked, setIsChecked] = useState(false); //또는 const stateHookArray = useState(false); const isChecked = stateHookArray[0]; const setIsChecked = stateHookArray[1];
state 변수에 저장된 값을 사용하려면 isChecked변수에 할당된 값을 활용한다.
- isChecked : state를 저장하는 변수
- setIsChecked : state, isChecked 를 변경하는 함수
- useState : state hook
- false : state의 초깃값으로 주는 값(isCehcked에는 false가 들어간다)
React 컴포넌트는 state가 변경되면 리렌더링된다.
state는 상태 변경 함수 호출로 변경해야
리렌더링이 되므로 강제로 변경시키면 안된다. 따라서 let이 아닌 const로 선언된것이고 강제로 함수내에서 해당 함수를 호출한다고하면 hook문제로 오류가 난다.import React, { useState } from "react"; import "./styles.css"; function App() { const [showPopup, setShowPopup] = useState(false); const togglePopup = () => { // Pop up 의 open/close 상태에 따라 // 현재 state 가 업데이트 되도록 함수를 완성하세요. if (showPopup) { setShowPopup(false); } else { setShowPopup(true); } }; return ( <div className="App"> <h1>Fix me to open Pop Up</h1> {/* 버튼을 클릭했을 때 Pop up 의 open/close 가 작동하도록 button tag를 완성하세요. */} <button className="open" onClick={togglePopup}> Open me </button> {showPopup ? ( <div className="popup"> <div className="popup_inner"> <h2>Success!</h2> <button className="close" onClick={togglePopup}> Close me </button> </div> </div> ) : null} </div> ); } export default App;
React는 하향식(top-down)이자 단방향 데이터흐름방식을 따른다. 이때 하위 컴포넌트에서 일나는 이벤트가 상위 컴포넌트에 영향을 주는 경우가 존재하는데 마치 역방향 데이터 흐름처럼 보인다.
React에서는 이러한 상황에서는 아래와같이 해결한다.
1. 상위 컴포넌트에 정의된 state함수를 하위 컴포넌트에 props로 전달.
2. 하위 컴포넌트에서 이 콜백함수를 실행
/* 부모 컴포넌트 */ const Top = () =>{ //1. State선언 const [deleteMessage, setdeleteMessage] = useState("Bye"); //2. 전달할 콜백함수 선언 const handleMessage = (delivered) => { setdeleteMessage(delivered); } console.log(deleteMessage) // 처음 Bye출력, 클릭후 Hi 출력 return ( <div> <Bottom Message={deleteMessage} onClick_delete={handleMessage}/> </div> ); }; /* 자식 컴포넌트 */ // 매개변수는 {Message, onClick_delete}각각 deleteMessage, handleMessage가 들어가 있음. //({})가 아닌 ()으로 받으면 객체자체(props)로 받는거고 ({})는 구조분해할당과 같은 역할 const Bottom = ({ Message, onClick_delete }) => { const handleMessageClick = ()=>{ onClick_delete("Hi"); //Hi대신 Bottom에서 만든 변수나 값을 전달할수 있음. } return ( <button onClick={handleMessageClick}>삭제</button> ); };
1.하위 컴포넌트에 전달하고자 하는 값(data)과 속성을 정의한다.
2. props를 이용하여 정의된 값과 속성을 전달한다.
3. 전달받은 props를 렌더링한다.
function Parent() { return ( <div className="parent"> <h1>I'm the parent</h1> <Child attribute={value} /> <Child text ={"Hello Child"} /> </div> ); }; function Child(props) { return ( <div className="child"> <p>{props.text}</p> <p>{props.attribute}</p> </div> ); };
- props에 key, value형식으로 들어가므로 전달시 key value 형식으로 전달하면 된다.
function Parent() { return ( <div className="parent"> <h1>I'm the parent</h1> <Child>I'm the eldest child</Child> </div> ); }; function Child(props) { return ( <div className="child"> <p>{props.children}</p> </div> ); };
- Child 태그사이에 값을 넣어 전달하고 props.children으로 받는 방법도 있다.
function Counter() {
const [count, setCount] = useState(0);
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + count);
}, 3000);
}
return (
<div>
<p>You clicked {count} times</p>
<button className = "countbtn"onClick={() => setCount(count + 1)}>
countbtn
</button>
<button className = "handlebtn" onClick={handleAlertClick}>
handlebtn
</button>
</div>
);
}
비슷한예로 아래와같은 현상도 있다.
import React, { useState } from "react"
function App() {
const [num, setNum] = useState(1)
async function plus() {
setNum(num + 1)
setNum(num + 1)
setNum(num + 1)
}
async function minus() {
setNum(num - 1)
}
return (
<div className="App">
<h1>{num}</h1>
<button onClick={plus}>PLUS</button>
<button onClick={minus}>MINUS</button>
</div>
);
}
export default App;
PLUS를 실행해도 1만 증가된 모습을 볼수 있다. 아까말한것처럼 현재의 lexical scope를 기억하고 실행해서이다. React는 16ms동안 실행한 동일한 setState를 일괄처리를 하므로 최종적으로 마지막의 setState만 실행이된다.
해결방안은 setNum(num => num + 1)으로 바꾸면된다.
여러번 전달받는 함수들은 큐에 저장되어 순서대로 실행된다. 따라서 큐에서 먼저 수행된 함수의 결과로 반영된 state값이 다음 수행할 함수의 인자로 들어가게 되므로, 항상 최신의 state를 유지하게 된다
https://garve32.tistory.com/39
import logo from './logo.svg';
import './App.css';
import { useState } from 'react';
let t = [
{
"itemId": 1,
"quantity": 1
},
{
"itemId": 5,
"quantity": 7
},
]
let tt = [{
"itemId": 1,
"quantity": 1
}]
function App() {
const [temp, setTemp] = useState(t);
const [temp2, setTemp2] = useState(temp.map(el => el.itemId));
// 위에 두개는 최초 랜더링될때만 실행됨.
console.log(temp);
console.log(temp2);
return (
<div className="App">
<button onClick={() => { setTemp(tt) }}>TEST</button>
</div>
);
}
export default App;
state prop를 이용하여 간단한 게시판 만들기를 해봤다.
개념학습부터 활용까지 2일이 걸렸지만 배운게 많았다.
각각에 대한 개념은 이 블로그에 따로 정리를 했고 SPA를 구현하기에 매우 적합한 문법인것 같다.
기능별로 컴포넌트화를 시키고 top-down방식으로 개발을 하니 확실히 유지보수에 편할것 같고 개발하는 데 있어서도 분업과 협업에 있어서 매우 적합하다고 생각이 들었다.
소스 주소
https://github.com/kkdy21/fe-sprint-react-twittler-state-props.git