* 아래 코드를 확인하기 전 알아야 할 것이 있다. React는 같은 레벨에서 데이터를 주고 받을 수 없기 때문에 부모 컴포넌트로 데이터를 전달하고, 부모 데이터로부터 데이터를 전달 받아야 한다.
- 위와 같이 Event를 통해 부모 컴포넌트에서 자식 컴포너트로부터 데이터를 전달받고, 자식 컴포넌트로 데이터를 전달하는 것을 확인 할 수 있다.
- 사용자 입력, 입력 화면
- App.js
import { useEffect, useRef, useState } from "react";
import "./App.css";
import DiaryEditor from "./DiaryEditor";
import DiaryList from "./DiaryList";
import Lifecycle from "./Lifecycle";
function App() {
const [data, setData] = useState([]);
const dataId = useRef(0);
//API 사용
const getData = async () => {
const res = await fetch(
`https://jsonplaceholder.typicode.com/comments`
).then((res) => res.json());
console.log(res);
const initData = res.slice(0, 20).map((it) => {
return {
author: it.email,
content: it.body,
emotion: Math.floor(Math.random() * 5) + 1,
created_date: new Date().getTime(),
id: dataId.current++,
};
});
setData(initData);
};
useEffect(() => {
getData();
}, []);
const onCreate = (author, content, emotion) => {
const created_date = new Date().getTime();
const newItem = {
author,
content,
emotion,
created_date,
id: dataId.current,
};
dataId.current += 1;
setData([newItem, ...data]);
};
const onRemove = (targetId) => {
console.log(`${targetId}가 삭제되었습니다.`);
const newDiaryList = data.filter((it) => it.id !== targetId);
setData(newDiaryList);
};
const onEdit = (targetId, newContent) => {
setData(
data.map((it) =>
it.id === targetId ? { ...it, content: newContent } : it
)
);
};
return (
<div className="App">
<Lifecycle />
<DiaryEditor onCreate={onCreate} />
<DiaryList onEdit={onEdit} onRemove={onRemove} diaryList={data} />
</div>
);
}
export default App;
- DiaryEditor 컴포넌트에 해당하는 부분이다.
- DiaryEditor.js
import React, { useRef, useState } from "react";
const DiaryEditor = ({ onCreate }) => {
// App.js로부터 onCreate Prop을 전달 받는다.
const authorInput = useRef();
const contentInput = useRef();
const [state, setState] = useState({
author: "",
content: "",
emotion: 1,
});
const handleChangeState = (e) => {
setState({
...state,
[e.target.name]: e.target.value,
});
};
const handleSubmit = () => {
if (state.author.length < 1) {
//focus
authorInput.current.focus();
return;
}
if (state.content.length < 5) {
//focus
contentInput.current.focus();
return;
}
onCreate(state.author, state.content, state.emotion);
alert("저장 성공");
setState({
author: "",
content: "",
emotion: "",
});
};
return (
<div className="DiaryEditor">
<h2>오늘의 일기</h2>
<div>
<input
ref={authorInput}
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}
>
<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;
- App.js
function App() {
const [data, setData] = useState([]);
const dataId = useRef(0);
.......
const onCreate = (author, content, emotion) => {
const created_date = new Date().getTime();
const newItem = {
author,
content,
emotion,
created_date,
id: dataId.current,
};
dataId.current += 1;
setData([newItem, ...data]);
};
.......
- onCreate prop은 새로운 일기 하나를 새롭게 생성하는 prop이다.
const [state, setState] = useState({
author: "",
content: "",
emotion: 1,
});
- 최초의 화면에 useState()의 기본값에 의해 author="", content="", emotion=1로 값이 나타날 것이다.
return (
<div className="DiaryEditor">
<h2>오늘의 일기</h2>
<div>
<input
ref={authorInput}
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}
>
<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>
);
};
- 위 코드에 의해
- 위와 같이 최초의 화면이 나타난다.
- 값을 입력하는 태그인 input, textarea, select와 같이 입력 필드의 값이 변경되면 이벤트가 트리거 된다.
- 해당 event 객체를 전달하게 된다.
- target.name은 해당 HTML 요소의 name 속성의 값을 가져온다. target.value는 해당 HTML 요소의 value 속성의 값을 가져온다.
const handleChangeState = (e) => {
setState({
...state,
[e.target.name]: e.target.value,
});
};
- handleChangeState의 함수는 이벤트 객체를 받아서 useState의 수정자인 setState를 이용하여 state의 값을 변경하고 리렌더링 한다.
- ...state는 스프레드를 통해 기존의 값은 유지한 채 새롭게 추가된 속성의 값을 이벤트 객체에 추가한다.
- 화면에 값을 입력하고 저장하기 버튼을 누르게 되면 handleSubmit 함수가 실행되게 된다.
const handleSubmit = () => {
if (state.author.length < 1) {
//focus
authorInput.current.focus();
return;
}
if (state.content.length < 5) {
//focus
contentInput.current.focus();
return;
}
onCreate(state.author, state.content, state.emotion);
alert("저장 성공");
setState({
author: "",
content: "",
emotion: "",
});
};
- if문을 통해 현재 state 객체에 저장된 값들을 조건에 맞게 검증하고 useRef를 통해 검증을 실패할 경우 해당 부분으로 focus()하게 지정한다. (useRef에 대해서는 아래에서 설명할 것이다.)
- 검증에 전부 통과하게 되며, onCreate() 함수가 호출되게 되고, 파라미터로 author, content, emotion이 전달되며, author, content, emotion 순서로 "", "", 1로 초기화 한다.
function App() {
const [data, setData] = useState([]);
const dataId = useRef(0);
.......
const onCreate = (author, content, emotion) => {
const created_date = new Date().getTime();
const newItem = {
author,
content,
emotion,
created_date,
id: dataId.current,
};
dataId.current += 1;
setData([newItem, ...data]);
};
.......
- App.js에서 전달 받은 onCreate 함수를 보게 되면, 전달받은 파라미터를 기반으로 newItem 객체를 생성하고, App.js의 useState인 setData를 통해 새롭게 추가된 Item 객체를 추가하면 값을 변경한다.
- useRef
- useRef는 React에서 사용되는 훅 중 하나로, 주로 DOM 요소를 선택하거나 다른 React 요소와의 상호 작용을 위해 사용된다.
1. DOM 요소 선택 : useRef를 사용하여 DOM 요소에 접근하고 조작할 수 있다. useRef를 사용하여 특정 DOM 요소를 선택하고 그 요소의 스크롤 위치를 변경하거나 포커스를 설정할 수 있다.
2. 컴포넌트의 상태 유지 : useRef를 사용하면 컴포넌트의 렌더링과 상관없이 값이 유지된다. 이것은 상태가 변경되어도 컴포넌트가 다시 렌더링되지 않고 값을 변경하고 유지하려는 경우에 유용하다.
- 즉, useRef는
1. 변수 관리
2. DOM 요소 선택의 용도
자주 사용된다.
- 바로 위에서 작성한 DiaryEditor.js 코드를 보게되면
import React, { useRef, useState } from "react";
const DiaryEditor = ({ onCreate }) => {
const authorInput = useRef();
const contentInput = useRef();
.......
const handleSubmit = () => {
if (state.author.length < 1) {
//focus
authorInput.current.focus();
return;
}
if (state.content.length < 5) {
//focus
contentInput.current.focus();
return;
}
.......
return (
<div className="DiaryEditor">
<h2>오늘의 일기</h2>
<div>
<input
ref={authorInput}
name="author"
value={state.author}
onChange={handleChangeState}
/>
</div>
.......
<div>
<button onClick={handleSubmit}>일기 저장하기</button>
</div>
</div>
);
};
export default DiaryEditor;
- const authorInput = useRef(); 코드가 작성된 것을 볼 수 있다.
- 또 className="DiaryEditor"의 ref={authorInput}에 해당 변수가 사용된 것을 볼 수 있고, handleSubmit 컴포넌트에 author.current.focus()의 코드가 작성된 것을 볼 수 있다.
- 위의 코드는 handleSubmit 함수가 실행되고, if문에 true가 되어, authorInput.current.focus() 코드가 실행되면, authorInput은 useRef이기 때문에 해당 ref={authorInput}인 DOM으로 이동하고 해당 DOM에 focus()를 지정한다.
function App() {
const [data, setData] = useState([]);
const dataId = useRef(0);
.......
const onCreate = (author, content, emotion) => {
const created_date = new Date().getTime();
const newItem = {
author,
content,
emotion,
created_date,
id: dataId.current,
};
dataId.current += 1;
setData([newItem, ...data]);
};
- 마찬가지로 App.js에도 useRef()가 사용되었는데, 위 코드 같은 경우에는 최초의 값을 0을 넣고, 계속해서 리렌더링 되더라도 해당 변수의 이전 값이 기억되도록 사용하는 용도로 사용되었다.
- 기본적으로 값이 변경되고 리렌더링 되게 되면, 기존에 변수에 특정 값이 할당 되어 있다면 해당 값으로 돌아가거나 다른 외부에서 주입된 값이 들어 갈 수 있다.
- 하지만, useRef()를 사용하면 이전의 데이터가 리렌더링 되더라도 유지될 수 있다.
- API
https://jsonplaceholder.typicode.com/
- JSON 테스트 데이터를 가져오기 위해 위 사이트를 통해 가져오도록 한다.
- 위와 같이 해당 url을 통해 json 데이터를 가져올 수 있다.
- App.js
.......
//API 사용
const getData = async () => {
const res = await fetch(
`https://jsonplaceholder.typicode.com/comments`
).then((res) => res.json());
console.log(res);
const initData = res.slice(0, 20).map((it) => {
return {
author: it.email,
content: it.body,
emotion: Math.floor(Math.random() * 5) + 1,
created_date: new Date().getTime(),
id: dataId.current++,
};
});
setData(initData);
.......
- fetch()는 첫번째 인자로 url을 받고, 반환값으로 Promise 객체를 반환한다
- 응답에 성공했을 경우 fetch().then을 통해 응답에 대한 콜백 함수를 실행 할 수 있다. 해당 url을 통해 데이터를 받을 때 문자열 형태로 받기 때문에 .json()을 통해 해당 값을 json 형태로 변환한다.
- 결과적으로 res 변수에는 json 형태 즉, 객체가 담기게 된다.
- initData를 통해 객체를 20개만 가져오고, map을 통해 받아온 객체의 속성을 현재 개발중인 객체에 맞게 객체 형태를 만들어서 반환한다.
- 결과적으로 map을 통해 새롭게 생성된 20개의 객체가 setData를 통해 data 변수에 입력되고 리렌더링 된다.
'Portfolio, Project > Project(Programming)' 카테고리의 다른 글
React - useMemo / React.memo / useCallback (0) | 2023.09.20 |
---|---|
React - 리스트 렌더링 / 데이터 추가, 삭제, 수정 / useEffect (useEffect... 내용 저장 안함... ) (0) | 2023.09.19 |
회원가입 페이지 만들기 - 2 (Javascript) (0) | 2023.08.25 |
회원가입 페이지 만들기 - 1 (HTML, CSS) (0) | 2023.08.25 |
홈페이지 만들기 - 2 (0) | 2023.08.25 |