Загружаем каталог…
Загружаем каталог…
React_Day.3 자식 컴포넌트에 state 와 setter를 같이 내려주는 법, useEffect 의 의존성 배열, 배열 find 로 하나만 골라 보여주기, 그리고 클릭한 항목의 상세를 띄우는 게시판 구조까지 정리한 날. Day.2에서 props로 값을 부모에서 자식으로 내려보내는 것까지 했다. 오늘은 값뿐 아니라 값을 바꾸는 함수(setter)도 props로 내려보내서 자식이 부모의 state를 바꾸게 만든다. 그 과정에서 쓰는 useEffect 와 find 도 같이 익혔다. 1. children children = 태그와 태그 사이에 들어 있는 무언가 Day.2에서 본 props.children 을 한 줄로 다시 정리했다. <Card>여기</Card> 처럼 여는 태그와 닫는 태그 사이에 넣은 내용이 자식 컴포넌트에서 children 이라는 이름으로 넘어온다. 2. setter를 props로 내려보내기: 좋아요 / 싫어요 투표 자식( HateButton )이 버튼을 갖고, 부모( App )가 숫자를 갖는 구조다. 숫자( good , bad )는 부모의 state 이고, 자식은 그걸 props로 받아서 보여주기만 하다가 , 버튼을 누르면 부모가 내려준 setter 를 호출한다. // App.jsx (부모) import React, { useEffect, useState } from 'react' import HateButton from './HateButton' const App = () => { const [good, setGood] = useState(0) const [bad, setBad] = useState(0) useEffect(() => { console.log('투표수가 변경되었습니다') }, [good, bad]) // good, bad 둘 중 하나라도 바뀔 때마다 useEffect가 실행된다. return ( <div> {/* 좌측은 자식이 갖는 이름, 우측은 부모의 값. 그냥 이름=값 이라고 생각하면 된다. */} <HateButton good={good} bad={bad} setGood={setGood} setBad={setBad} /> <br /><br /> 좋아요:{good}<br /> 싫어요:{bad} </div> ) } export default App // HateButton.jsx (자식) import React from 'react' const HateButton = ({ good, bad, setGood, setBad }) => { return ( <div> <button onClick={() => setGood(good + 1)}>👍{good}</button> <button onClick={() => setBad(bad + 1)}>😡{bad}</button> </div> ) } export default HateButton 흐름은 이렇다. 처음에는 화면에 0 이 표시된다. 자식의 버튼을 눌러 setGood 을 호출하면 부모의 good 값이 바뀐다. state가 바뀌면 App 이 다시 렌더링되고, {good} 자리에 새 값이 반영된다. 자식에게 내려가는 good 도 새 값이 된다. useEffect 도 실행된다. 직접 실행해서 좋아요 2번, 싫어요 1번을 눌러 보니 화면은 좋아요:2 싫어요:1 이 되었다. 여기서 기억할 점은 state는 부모 한 곳에만 있다 는 것이다. 자식은 자기만의 숫자를 따로 갖지 않고, 부모의 것을 빌려 쓴다. 이렇게 하면 부모가 두 숫자를 한꺼번에 알고 있어서 나중에 "좋아요 비율 계산" 같은 걸 부모에서 바로 할 수 있다. (이런 패턴을 흔히 "state 끌어올리기"라고 부른다.) 이름이 같아서 헷갈리는 부분 good={good} 에서 왼쪽은 자식이 받을 props 이름 , 오른쪽은 부모가 가진 변수 다. 이름을 같게 맞춰 두면 자식에서 ({ good, bad, setGood, setBad }) 로 구조 분해해서 바로 쓸 수 있어서 편하다. 이름을 다르게 줘도 동작하지만 자식에서 받는 이름도 그에 맞춰야 한다. 3. useEffect: 값이 바뀔 때 실행하기 useEffect(() => { console.log('투표수가 변경되었습니다') }, [good, bad]) 첫 번째 인자: 실행할 함수 두 번째 인자: 의존성 배열 . 여기에 적은 값 중 하나라도 바뀌면 함수가 다시 실행된다. 배열을 어떻게 쓰느냐에 따라 실행 시점이 달라진다. 두 번째 인자 실행 시점 생략 렌더링될 때마다 [] 처음 한 번만 [good, bad] 처음 한 번 + good 또는 bad 가 바뀔 때마다 필기에는 "바뀔 때마다 실행된다"고만 적었는데, 실제로 돌려 보면 처음 화면이 그려질 때도 한 번 실행된다. effect good=0 bad=0 ← 첫 렌더링 직후 (아직 아무것도 안 눌렀다) effect good=1 bad=0 ← 👍 첫 번째 클릭 effect good=2 bad=0 ← 👍 두 번째 클릭 effect good=2 bad=1 ← 😡 클릭 그래서 "투표수가 변경되었습니다"라는 메시지가 투표하기 전에도 한 번 찍힌다. 정말 "변경됐을 때만" 하고 싶다면 첫 실행인지 구분하는 처리를 따로 해야 한다. 또 하나, 개발 모드에서 <React.StrictMode> 로 감싸져 있으면 첫 실행이 두 번 찍힌다. 이것도 실제로 확인했다. 개발 중에 부작용(effect)이 안전하게 짜여 있는지 검사하려고 React가 일부러 두 번 돌리는 것이고, 배포 빌드에서는 한 번만 실행된다. 로그가 두 번 찍힌다고 코드가 잘못된 게 아니다. 4. 배열 find: 조건에 맞는 첫 번째 하나 찾기 map 이 배열 전체를 돌면서 새 배열을 만든다면, find 는 조건에 맞는 첫 번째 요소 하나 를 돌려준다. const people = [ { name: 'A', age: 25 }, { name: 'B', age: 20 }, { name: 'C', age: 11 }, { name: 'D', age: 63 }, ] const n = people.find((p) => p.age > 20) console.log(n) // { name: 'A', age: 25 } 나이가 20 초과인 사람은 A 와 D 두 명이지만, find 는 처음 찾은 A 에서 멈춘다. 둘 다 필요하면 filter 를 쓴다. people.filter((p) => p.age > 20) // [{A}, {D}] 조건에 맞는 전부를 배열로 숫자 배열로도 해 보았다. const arr = [1, 3, 5, 7] arr.find((a) => a > 10) // undefined ← 조건에 맞는 게 없으면 undefined arr.find((a) => a <= 5) // 1 ← 5 이하 중 '첫 번째'. 1, 3, 5가 다 해당되지만 1에서 멈춘다 arr.findIndex((a) => a > 10) // -1 ← 위치를 알려주는 findIndex는 못 찾으면 -1 주의할 점은 못 찾으면 undefined 가 나온다 는 것이다. 이 값을 그대로 undefined.name 처럼 쓰면 에러가 나므로, 찾은 결과가 없을 수도 있는 상황에서는 확인을 한 번 거쳐야 한다. (5번에서 selPost1 && ... 로 확인하는 것도 같은 이유다.) 필기의 콜백 변수 이름이 바깥 배열과 같은 pro / pro1 식이라 헷갈릴 수 있어서, 위 예시는 콜백 변수를 p 로 짧게 바꿨다. 바깥 변수와 같은 이름을 쓰면 안쪽이 바깥을 가려서(shadowing) 읽기 어려워진다. 5. 버튼으로 사용자 고르기: find + 자식에 객체 통째로 넘기기 사용자 목록 버튼을 누르면 그 사람의 카드가 아래에 표시되는 예제다. 저장하는 것은 객체가 아니라 id 하나 다. // App.jsx import React, { useState } from 'react' import ProCard from './ProCard' const App = () => { const user = [ { id: 1, name: '사용자A', age: 14, job: '개발자' }, { id: 2, name: '사용자B', age: 22, job: '교수' }, { id: 3, name: '사용자C', age: 33, job: '디자이너' }, { id: 4, name: '사용자D', age: 41, job: '경비원' }, ] const [selectUserId, setSelectUserId] = useState(1) // 내가 클릭한 id와 user의 id가 같은 사람을 찾는다. const selUser = user.find((u) => u.id === selectUserId) return ( <div> {user.map((a) => { return ( <button key={a.id} onClick={() => setSelectUserId(a.id)}> {a.name} </button> ) })} <ProCard user1={selUser} /> </div> ) } export default App // ProCard.jsx import React from 'react' const ProCard = ({ user1 }) => { return ( <div> <h1>{user1.name} {user1.age} {user1.job}</h1> </div> ) } export default ProCard 실행해 보면 처음에는 첫 사람의 카드가 뜨고, 세 번째 버튼을 누르면 카드가 그 사람 것으로 바뀐다. (처음) 사용자A 14 개발자 (3번째 버튼 클릭) 사용자C 33 디자이너 구조를 정리하면 이렇다. state에는 "누가 선택됐는지"를 나타내는 id만 저장한다. 렌더링할 때마다 find 로 id에 해당하는 실제 객체를 찾아서 자식에게 넘긴다. 자식( ProCard )은 받은 객체만 보여준다. id만 저장하면 사용자 목록의 값이 바뀌어도 선택된 사람의 최신 정보를 항상 find 로 다시 얻는다. 객체를 통째로 state에 복사해 두면 원본이 바뀌었을 때 복사본은 옛날 값을 들고 있게 될 수 있다. 이 코드에는 한 가지 가정이 있다. ProCard 는 user1 이 반드시 있다고 믿고 user1.name 을 읽는다. 지금은 초기값이 1 이고 버튼도 목록에서 만들어서 항상 찾아지지만, 만약 목록에 없는 id가 들어가면 find 가 undefined 를 돌려주고 ProCard 에서 에러가 난다. 실제 서비스라면 {selUser && <ProCard user1={selUser} />} 처럼 막아 둬야 한다. 6. 게시판: 클릭한 글의 상세 보기 목록에서 제목을 클릭하면 아래에 내용이 나오는 간단한 게시판이다. import React, { useState } from 'react' const App = () => { const [posts, setPosts] = useState([ { id: 1, title: '첫번째 제목', content: '첫번째 내용' }, { id: 2, title: '두번째 제목', content: '두번째 내용' }, { id: 3, title: '세번째 제목', content: '세번째 내용' }, ]) const [selPost1, setSelPost1] = useState(null) // 처음에는 null. 세번째 제목을 클릭하면 세번째 객체를 가져와서 selPost1에 저장한다. // 다른 글을 클릭할 때마다 selPost1이 그 글의 객체로 바뀐다. const selPost = (post) => { setSelPost1(post) // 클릭한 객체 } return ( <div className="board-app"> <h1>Board</h1> <div className="board-li"> <input placeholder="제목 입력" /><br /> <textarea placeholder="내용 입력" /> <h2>Board List</h2> {posts.map((x) => { return ( <div key={x.id} className="board-item" onClick={() => selPost(x)}> <h2>{x.title}</h2> </div> ) })} </div> <hr /> {selPost1 && ( <div> <h2>{selPost1.title}</h2> <h2>{selPost1.content}</h2> </div> )} </div> ) } export default App 실행해 보면 처음에는 상세 영역이 없고( null && ... 은 아무것도 그리지 않는다), 세 번째 글을 누르면 세번째 제목 / 세번째 내용 , 첫 번째 글을 누르면 그 글로 바뀐다. 여기서 쓴 두 가지 패턴이 오늘 내용의 핵심이다. useState(null) 로 "아직 선택 없음"을 표현 하고, {selPost1 && (...)} 로 선택됐을 때만 그린다. Day.2의 조건부 렌더링이다. 객체는 항상 truthy이고 null 은 falsy라서 0 && 같은 함정도 없다. map 으로 목록을 만들고, 각 항목에 onClick 으로 "내가 누구인지"를 넘긴다. onClick={() => selPost(x)} 처럼 화살표 함수로 감싸서 클릭했을 때 호출되게 한다. onClick={selPost(x)} 라고 쓰면 렌더링 중에 바로 호출되어 버린다. 객체 대신 id를 저장하면 더 안전하다 위 코드는 클릭한 객체 자체 를 state에 저장한다. 5번처럼 id만 저장 하고 find 로 찾는 방식으로 바꾸면 이런 차이가 생긴다. 글을 삭제하는 기능이 있다고 해 보자. const [selId, setSelId] = useState(null) const sel = posts.find((p) => p.id === selId) // 목록에서 찾는다 // 삭제: filter로 해당 id만 빼고 새 배열을 만든다 const del = (id) => setPosts(posts.filter((p) => p.id !== id)) {sel && <div>{sel.title}/{sel.content}</div>} 글을 선택한 상태에서 그 글을 삭제하면, 목록에서 사라졌으니 find 가 undefined 를 돌려주고 상세 영역도 같이 사라진다. 실제로 돌려 보니 삭제 후 목록이 1개로 줄고 상세 영역은 없어졌다. 객체를 직접 저장했다면 목록에서는 지워졌는데 상세 영역에는 삭제된 글이 계속 남아 있었을 것이다. 지금 예제는 삭제가 없어서 둘 다 문제없지만, 기능이 늘어나면 id 방식이 덜 꼬인다. 아직 미완성인 부분 입력창( input , textarea )은 모양만 있고 state와 연결되어 있지 않다. 입력해도 글이 추가되지 않는다. Day.2에서 한 제어 컴포넌트( value 와 onChange )와 새 배열을 만들어 추가하는 패턴( setPosts([...posts, 새글]) )을 합치면 글쓰기까지 만들 수 있다. 이건 다음에 이어서 해 볼 부분이다. 마무리 state는 부모 한 곳에 두고 , 값과 setter를 props로 내려서 자식이 부모의 state를 바꾸게 한다. useEffect(함수, [값들]) 은 처음 한 번 + 배열 안의 값이 바뀔 때마다 실행된다. 바뀔 때만 실행되는 게 아니라 처음에도 실행 되고, 개발 모드의 StrictMode 에서는 처음에 두 번 실행된다. find 는 조건에 맞는 첫 번째 하나 를 돌려주고, 못 찾으면 undefined 다. 전부 필요하면 filter 를 쓴다. "선택된 항목"은 객체 대신 id를 state에 저장하고 find 로 찾으면 원본이 바뀌어도 최신 값을 쓴다. null 을 "선택 없음"으로 쓰고 {값 && (...)} 으로 있을 때만 그린다. find 결과는 undefined 일 수 있어서 쓰기 전에 확인한다. Tags : React props useState useEffect find 조건부렌더링 프론트엔드 개발자 학습기록
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
React_Day.3. React_Day.3 자식 컴포넌트에 state 와 setter를 같이 내려주는 법, useEffect 의 의존성 배열, 배열 find 로 하나만 골라 보여주기, 그리고 클릭한 항목의 상세를 띄우는 게시판 구조까지 정리한 날. Day.2에서 props로 값을 부모에서 자식으로 내려보내는 것까지 했다. 오늘은 값뿐 아니라 값을 바꾸는 함수(setter)도 props로 내려보내서 자식이 부모의 state를 바꾸게 만든다. 그 과정에서 쓰는 useEffect 와 find 도 같이 익혔다. 1. children children = 태그와 태그 사이에 들어 있는 무언가 Day.2에서 본 props.children 을 한 줄로 다시 정리했다. 여기 처럼 여는 태그와 닫는 태그 사이에 넣은…
Открыть источник