1-4 React에서 리스트 데이터 추가하기

밥이·2022년 2월 18일
0

React Project

목록 보기
4/14

React에서 리스트 데이터 추가하기

저장하기 누를시 작성한 내용 리스트에 추가하기

컴포넌트 & 데이터 구조 생각해보기

진짜 리스트를 만들어 보기 전에 이런 작업을 어떻게 해야할지 그림으로 조금 생각을 해보는게 좋음.
실제로 리액트를 가지고 프론트엔드를 개발하다보면 컴포넌트 트리를 그려보는 순간도 굉장히 많음
그래서 이런 유연한 사고를 따라해보기 위해, 컴포넌트 트리를 그려보면서 연습하는게 사고력 증진에 엄청 도움됨.
그러면 DiaryEditor 컴포넌트에서 작성한 일기를 DiaryList 컴포넌트로 추가를 해줘야 하는데 그러면 어떻게 해야하나?
data는 지금 하나의 Item만 가지고 있는 배열이라고 가정 하면, 이 data를 props로 내려받은 DiaryList 컴포넌트는 하나의 Item만 렌더링 하고 있을꺼임,

그런데 이때 일기 작성 폼인 DiaryEditor 컴포넌트에서 새로운 일기가 작성되면, App 컴포넌트가 DiaryEditor 한테 props로 전달한 setData함수를 호출하게되고, (setData함수는 data의 값을 바꾸는 상태변화 함수임, 새로운Item이 추가되도록 data상태의 값을 바꿀꺼임)
일기를 추가하면 data state가 변경이 됐으니까, DiaryList 컴포넌트에게 전달하는 data 즉, 추가된 data 까지 포함해서 새로운 데이터도 DiaryList 에 내려가게되고, 원래있던 Item1과 새로받은 Item2를 렌더링 하게됨.

이렇게 하면 단방향으로만 데이터가 흐르는 문제 에 대해서 해결할 수 있음.

정리

App 컴포넌트는 일기를 작성하는 DiaryEditor 컴포넌트와 일기 작성시 리스트를 추가하는 DiaryList 컴포넌트가 함께 사용 할 일기 [data, setData] state를 [빈배열]로 가지고 있음.

이 상태에서 DiaryList 한테는 현재 App 컴포넌트가 가진 일기 배열 data state를 넘겨줌.

그러면 App 컴포넌트가 가지고 있는 data state가 바뀌면 DiaryList 에 있는 data state도 알아서 렌더링을 하게되고, 일기가 추가 되면 추가되고, 삭제가 되면 삭제되는 렌더링이 됨.

그 다음에 지금 해야 할 일은 DiaryEditor 컴포넌트 에서 일기를 작성하고 나서 '저장하기'를 할 때 DiaryList 컴포넌트에 추가가 되는 작업을 해야하는데,
그럴려면 App컴포넌트에 있는 data상태를 변화시키면됨.
왜? DiaryList 컴포넌트는 App 컴포넌트에 있는 일기 data state를 props로 받아 사용하기 있기 때문임.

그래서 App 컴포넌트에 있는 data state가 바뀌면 DiaryList 컴포넌트에 있는 data도 바뀌게됨.

일기를 작성하고 저장 했을 때 DiaryList 컴포넌트에 반영을 할려면 일단 App 컴포넌트에 onCreate()라는 함수를 만듬 이 함수의 역할은 일기를 저장했을때, 일기 작성폼을 받아 새로운 일기 data를 추가 하는 함수.

onCreate()라는 함수는 작성자,컨텐츠,감정을 전달받고,
DiaryEditor 컴포넌트는 onCreate()함수를 App 컴포넌트로 부터 props로 받아서 handleSumit()로 일기 저장이 일어났을때 onCreate()함수를 호출하여 그러면서 DiaryEditor 컴포넌트가 가지고 있는 작성자,컨텐츠,감정 데이터를 App 로 전달을 해줌.

그러면 App 컴포넌트에 있는 onCreate()함수는 DiaryEditor 컴포넌트에서 보낸 작성자, 컨텐츠, 감정점수를 받아서 현재 일기가 추가되는 시간 객체를 만들어 주고, 받은 작성자, 컨텐츠, 감정점수는 newItem객체를 만들어 저장하고, setData([])함수를 호출해서 newItem과 원래 있었던 ...data 일기 데이터를 가져와 변경시킴.

App.js

import { useRef, useState } from 'react';
import './App.css';
import DiaryEditor from './DiaryEditor';
import DiaryList from './DiaryList';


function App() {

const [data, setData] = useState([]);

const dataId = useRef(0);// 어떤 DOM도 선택하지 않고 0이란 값만 들어있음.

const onCreate = (author, content, emotion) => {
	const create_date = new Date().getTime();
	const newItem = {
		author,
		content,
		emotion,
		create_date,
		id: dataId.current
	}
	dataId.current += 1; // newItem이 추가될때마다 0번 id는 1씩 증가해야함
	setData([newItem, ...data]) // 새로운 일기를 추가하면 제일 위로 올라와야하니까. newItem을 앞으로 설정
}															// ...data는 data state의 객체 전부 펼치기
return (
	<div className="App">
		<DiaryEditor onCreate={onCreate} />
		<DiaryList dummyList={data} />
	</div>
);
}

export default App;

DiaryEditor

import { useRef, useState } from "react";

const DiaryEditor = ({ onCreate }) => {

const authorInput = useRef();
const contentInput = useRef();

// 같은 기능을 가진 인풋들은 state객체로 합치기
const [state, setState] = useState({
	author: "",
	content: "",
	emotion: 1,
});

// author, content, emotion Input 변경시 발생하는 함수
const handleChangeState = (e) => {
	setState({
		...state,
		[e.target.name]: e.target.value,

		// 객체안에서 key를 작성하는 []배열의 비구조화 할당
		// 즉, [e.target.name] 이라는 key는 전달받은 name이 author면 author의 인풋값을 받아 업데이트.
	})
};

// 저장할시 실행
const handleSubmit = () => {
	if (state.author.length < 1) {
		// DOM요소를 선택하는 useRef()기능으로 생성한 authorInput객체는
		// 현재 가르키는 값을 current로 불러와서 사용.
		// 즉, authorInput.current는 author인풋을 가르키고, 거기에 focus()를 사용 
		authorInput.current.focus();
		return
	} else if (state.content.length < 1) {
		// focus
		contentInput.current.focus();
		return
	}

	// props로 받은, onCreate()함수를 호출해 사용자가 입력한 값들을 보내줌
	onCreate(state.author, state.content, state.emotion);
	alert('저장 완료!')
	// 일기 내용 빈값으로 초기화
	setState({
		author: "",
		content: "",
		emotion: 1,
	});
}

return (
	<div className="DiaryEditor">
		<h2>게시판</h2>
		<div>
			<input
				ref={authorInput} // useRef를 통해 Input태그에 접근할 수 있게됨
				name="author"
				value={state.author}
				onChange={handleChangeState}
			/>
		</div>

		<div>
			<textarea
				ref={contentInput}
				name="content"
				value={state.content}
				onChange={handleChangeState}
			/>
		</div>

		<div>
			<select
				name="emotion"
				value={state.emotion}
				onChange={handleChangeState} // 옵션 선택시 onChange발동
			>
				<option value={1}>1</option>
				<option value={2}>2</option>
				<option value={3}>3</option>
				<option value={4}>4</option>
				<option value={5}>5</option>
			</select>
		</div>

		<div>
			<button onClick={handleSubmit}>저장하기</button>
		</div>
	</div>
);
};
export default DiaryEditor;

DiaryList

import { useState } from "react";
import DiaryItem from './DiaryItem';

const DiaryList = ({ dummyList }) => {
    console.log(dummyList);
    return (
	<div className='DiaryList'>
		<h2>일기 리스트</h2>
		<h4>{dummyList.length}개의 일기가 있습니다.</h4>

		<div>

			{dummyList.map((it) => {
				// key는 고유한 id를 가지고있는 값으로 설정해주는게 좋음
				return (
					// ...을 이용해 it안에 있는 모든 데이터를 props으로 <DiaryItem/>에 전달
					<DiaryItem key={it.id} {...it} />

				)
				// dummyList.map을 통해서 리스트 아이템들을 렌더링 하고 있는데, 
				// 우리 프로젝트는 일기 아이템을 삭제, 수정 등 기능들을 map함수 안에 다 넣어줘야함.
				// 아이템 하나하나를 수정,삭제 기능을 여기 컴포넌트 안에 다 만들어줘야함.
				// 그렇게 되면 에러가 날 확률이 높아지고, 딱히 좋지 않은 방법임
				// 그래서 아이템을 렌더하는 컴포넌트를 따로 만들어 분활.
			})}
		</div>
	</div>
);
};

// undefined으로 전달될것 같은 Props들을 기본값을 설정할수있음
// 만약 undefined이나 이상한 값이 전달되면 빈 배열로 기본값 설정.
DiaryList.defaultProps = {
    dummyList: [],
}

export default DiaryList;

0개의 댓글