인프런 커뮤니티 질문&답변

DoYoung Ahn님의 프로필 이미지
DoYoung Ahn

작성한 질문수

[2024] 한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지

11.3) Context 분리하기

소스코드 링크 확인 부탁드려요.

작성

·

352

1

안녕하세요.
 
공식도큐먼트를 봐도 생소하고 어려운 개념들을 실제 코드를 통해 쉽고 친절하게 전달해주셔서 초보자입장에서 감사히 잘 보고 있습니다.
 
해당 강좌의 소소코드링크가 보이지 않는데 확인부탁드리겠습니다.
 
감사합니다.

답변 2

1

이번 강의에서는 App.js , DiaryEditor.js , DiaryItem.js 파일만 수정하였습니다.
이번에는 전 강의에서 배운 useCallback과 React.memo를 통한 컴포넌트 렌더링 성능 최적화를 하였습니다.

 

이정환 강사님께서 보통 소스코드 올려주셨는데 여기에는 주소가 안보이네요. 제가 대신 이번 강의에서 수정한 파일 소스코드를 올리겠습니다. 혹시나 문제가 생길시 연락주시면 바로 삭제하겠습니다.

먼저 App.js 입니다.

import { useCallback, useEffect, useMemo, 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);

const getData = async () => {
const res = await fetch(
'https://jsonplaceholder.typicode.com/comments'
).then((res) => res.json());

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 = useCallback((author, content, emotion) => {
const created_date = new Date().getTime();
const newItem = {
author,
content,
emotion,
created_date,
id: dataId.current,
};
dataId.current += 1;
setData((data) => [newItem, ...data]);
}, []);

const onRemove = useCallback((targetId) => {
setData((data) => data.filter((it) => it.id !== targetId));
}, []);

const onEdit = useCallback((targetId, newContent) => {
setData((data) =>
data.map((it) =>
it.id === targetId ? { ...it, content: newContent } : it
)
);
}, []);

const getDiaryAnalysis = useMemo(() => {
const goodCount = data.filter((it) => it.emotion >= 3).length;
const badCount = data.length - goodCount;
const goodRatio = (goodCount / data.length) * 100;
return { goodCount, badCount, goodRatio };
}, [data.length]);

const { goodCount, badCount, goodRatio } = getDiaryAnalysis;

return (
<div className="App">
<DiaryEditor onCreate={onCreate} />
<div>전체 일기 : {data.length}</div>
<div>기분 좋은 일기 개수 : {goodCount}</div>
<div>기분 나쁜 일기 개수 : {badCount}</div>
<div>기분 좋은 일기 비율 : {goodRatio}</div>
<DiaryList onEdit={onEdit} onRemove={onRemove} diaryList={data} />
</div>
);
}

export default App;

DiaryEditor.js 입니다.

import React, { useRef, useState } from 'react';

const DiaryEditor = ({ onCreate }) => {
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) {
authorInput.current.focus();
return;
}

if (state.content.length < 5) {
contentInput.current.focus();
return;
}

onCreate(state.author, state.content, state.emotion);
alert('저장 성공!');
setState({
author: '',
content: '',
emotion: 1,
});
};

return (
<div className="DiaryEditor">
<h2>오늘의 일기</h2>
<div>
<input
ref={authorInput}
value={state.author}
onChange={handleChangeState}
name="author"
placeholder="작성자"
type="text"
/>
</div>
<div>
<textarea
ref={contentInput}
value={state.content}
onChange={handleChangeState}
name="content"
placeholder="일기"
type="text"
/>
</div>
<div>
<span>오늘의 감정점수 : </span>
<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 React.memo(DiaryEditor);

 DiaryItem.js  입니다.

import React, { useEffect, useRef, useState } from 'react';

const DiaryItem = ({
onEdit,
onRemove,
id,
author,
content,
emotion,
created_date,
}) => {
useEffect(() => {
console.log(`${id}번 째 아이템 렌더!`);
});
const [isEdit, setIsEdit] = useState(false);
const toggleIsEdit = () => setIsEdit(!isEdit);

const [localContent, setLocalContent] = useState(content);
const localContentInput = useRef();

const handleRemove = () => {
if (window.confirm(`${id}번째 일기를 정말 삭제하시겠습니까?`)) {
onRemove(id);
}
};

const handleQuitEdit = () => {
setIsEdit(false);
setLocalContent(content);
};

const handleEdit = () => {
if (localContent.length < 5) {
localContentInput.current.focus();
return;
}

if (window.confirm(`${id}번 째 일기를 수정하시겠습니까?`)) {
onEdit(id, localContent);
toggleIsEdit();
}
};

return (
<div className="DiaryItem">
<div className="info">
<span>
작성자 : {author} | 감정점수 : {emotion}
</span>
<br />
<span className="date">{new Date(created_date).toLocaleString()}</span>
</div>
<div className="content">
{isEdit ? (
<>
<textarea
ref={localContentInput}
value={localContent}
onChange={(e) => setLocalContent(e.target.value)}
/>
</>
) : (
<>{content}</>
)}
</div>

{isEdit ? (
<>
<button onClick={handleQuitEdit}>수정 취소</button>
<button onClick={handleEdit}>수정 완료</button>
</>
) : (
<>
<button onClick={handleRemove}>삭제하기</button>
<button onClick={toggleIsEdit}>수정하기</button>
</>
)}
</div>
);
};

export default React.memo(DiaryItem);

0

DoYoung Ahn님의 프로필 이미지
DoYoung Ahn
질문자

@tkddyd420

코드전체를 작성해주셨군요. 감사합니다!

DoYoung Ahn님의 프로필 이미지
DoYoung Ahn

작성한 질문수

질문하기