인프런 : https://www.inflearn.com/course/한입-리액트
강의 진행 중 사용된 소스코드 입니다.
https://codesandbox.io/s/chapt-1-otxc8
소스 : https://github.com/braverokmc79/react-with-one-bite
https://github.dev/braverokmc79/react-simple-diary
들어가며
1.강의 및 강사 소개
2. React 소개
3.오픈채팅방 입장 방법 및 비밀번호
JavaScript 기본
4.Hello World
5.변수와 상수
6.자료형과 형 변환
7.연산자
8.조건문
9.함수
10.함수표현식 & 화살표 함수
11.콜백함수
12.객체
13.배열
14.반복문
15.배열 내장 함수
JavaScript 응용
16.Truthy & Falsy
17.삼항 연산자
18.단락회로 평가
19.조건문 업그레이드
20.비 구조화 할당
21.Spread 연산자
22.동기 & 비동기
23.Promise - 콜백 지옥에서 탈출하기
24.async & await - 직관적인 비 동기 처리 코드 작성하기
25.API 호출하기
Node.js 기초
26.Node.js란?
27.Node.js & VsCode 설치하기
28.Node.js Hello World & Common JS
29.Node.js 패키지 생성 및 외부 패키지 사용하기
React.js 기초
30.Why React?
31.Create React App
32.JSX
33.State
34.Props
React 기본 - 간단한 일기장 프로젝트
35.프로젝트 소개
36.React에서 사용자 입력 처리하기
37.React에서 DOM 조작하기 - useRef
38.React에서 배열 사용하기 1 - 리스트 렌더링 (조회)
39.React에서 배열 사용하기 2 - 데이터 추가하기
40.React에서 배열 사용하기 3 - 데이터 삭제하기
41.React에서 배열 사용하기 4 - 데이터 수정하기
42.React Lifecycle 제어하기 - useEffect
43.React에서 API 호출하기
44.React developer tools
45.최적화 1 - useMemo
46.최적화 2 - React.memo
47.최적화 3 - useCallback
48.최적화 4 - 최적화 완성
49.복잡한 상태 관리 로직 분리하기 - useReducer
50.컴포넌트 트리에 데이터 공급하기 - Context
React 실전 프로젝트 - 감정 일기장 만들기
51.프로젝트 완성 예시
52.페이지 라우팅 0 - React SPA & CSR
53.페이지 라우팅 1 - React Router 기본
54.페이지 라우팅 2 - React Router 응용
55.프로젝트 기초 공사 1
56.프로젝트 기초 공사 2
57.페이지 구현 - 홈 (/)
58.페이지 구현 - 일기 쓰기 (/new)
59.페이지 구현 - 일기 수정 (/edit)
60.페이지 구현 - 일기 상세 (/diary)
61.(서브 챕터) 흔히 발생하는 버그 수정 하기
62.LocalStorage를 일기 데이터베이스로 사용하기
63.프로젝트 최적화
64.배포 준비 & 프로젝트 빌드하기
65.Firebase로 프로젝트 배포하기
66.Open Graph 설정하기
React 기본 - 간단한 일기장 프로젝트
35.프로젝트 소개
강의 :
36.React에서 사용자 입력 처리하기
강의 :
강의 진행 중 사용된 소스코드 입니다.
https://codesandbox.io/s/chapt-1-otxc8
0527 업데이트
30분 20초 즈음 css 스타일링을 시작하면서 추가되는 span태그에 대한 설명이 누락되었습니다.
다음과 같이 span 태그를 select 태그 위에 넣어주세요
<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>
이 문제를 제보해 주신 tkddyd420님 감사합니다.
https://www.inflearn.com/questions/544801
$ npx create-react-app .
DiaryEditor.js
import { useState } from "react"; const DiaryEditor = () => { const [state, setState] = useState({ author: "", content: "", emotion: 3 }); const handlerChangeState = (e) => { console.log(e.target.name, e.target.value); setState({ ...state, [e.target.name]: e.target.value }) } const handleSubmit = (e) => { console.log(state); alert("저장 성공"); } return (<div className="DiaryEditor"> <h2>오늘의 일기</h2> <div> <input name="author" value={state.author} onChange={handlerChangeState} /> {/* onChange={(e) => { // setState({...state, author: e.target.value }) // */} </div> <div> <textarea name="content" value={state.content} onChange={handlerChangeState} /> </div> <div> <span>오늘의 감정점수 : </span> <select name="emotion" value={state.emotion} onChange={handlerChangeState}> <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> <option value={6}>6</option> </select> </div> <div> <button onClick={handleSubmit}>일기 저장하기</button> </div> </div>); }; export default DiaryEditor;
App.css
.DiaryEditor{ border: 1px solid grey; text-align: center; padding: 20px; } .DiaryEditor input, textarea{ margin-bottom: 20px; width: 500px; padding: 10px; } .DiaryEditor textarea{ height: 150px; } .DiaryEditor select{ width: 300px; padding: 10px; margin-bottom: 20px; } .DiaryEditor button{ width: 500px; padding: 10px; cursor: pointer; }
37.React에서 DOM 조작하기 - useRef
강의 :
https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%EB%A6%AC%EC%95%A1%ED%8A%B8/unit/103524
import { useLayoutEffect, useRef, useState } from "react"; const DiaryEditor = () => { const authorInput = useRef(); const contentInput = useRef(); const [state, setState] = useState({ author: "", content: "", emotion: 3 }); const handlerChangeState = (e) => { console.log(e.target.name, e.target.value); setState({ ...state, [e.target.name]: e.target.value }) } const handleSubmit = (e) => { if (state.author.length < 1) { //alert("작성자는 최소 1글자 이상 입력해주세요."); authorInput.current.focus(); return; } if (state.content.length < 5) { //alert("일기 본문은 최소 5글자 이상 입력해주세요."); contentInput.current.focus(); return; } alert("저장 성공"); } return (<div className="DiaryEditor"> <h2>오늘의 일기</h2> <div> <input name="author" ref={authorInput} value={state.author} onChange={handlerChangeState} /> {/* onChange={(e) => { // setState({...state, author: e.target.value }) // */} </div> <div> <textarea name="content" ref={contentInput} value={state.content} onChange={handlerChangeState} /> </div> <div> <span>오늘의 감정점수 : </span> <select name="emotion" value={state.emotion} onChange={handlerChangeState}> <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> <option value={6}>6</option> </select> </div> <div> <button onClick={handleSubmit}>일기 저장하기</button> </div> </div>); }; export default DiaryEditor;
38.React에서 배열 사용하기 1 - 리스트 렌더링 (조회)
강의 :
DiaryEditor.js
import { useLayoutEffect, useRef, useState } from "react"; const DiaryEditor = () => { const authorInput = useRef(); const contentInput = useRef(); const [state, setState] = useState({ author: "", content: "", emotion: 3 }); const handlerChangeState = (e) => { console.log(e.target.name, e.target.value); setState({ ...state, [e.target.name]: e.target.value }) } const handleSubmit = (e) => { if (state.author.length < 1) { //alert("작성자는 최소 1글자 이상 입력해주세요."); authorInput.current.focus(); return; } if (state.content.length < 5) { //alert("일기 본문은 최소 5글자 이상 입력해주세요."); contentInput.current.focus(); return; } alert("저장 성공"); } return (<div className="DiaryEditor"> <h2>오늘의 일기</h2> <div> <input name="author" ref={authorInput} value={state.author} onChange={handlerChangeState} /> {/* onChange={(e) => { // setState({...state, author: e.target.value }) // */} </div> <div> <textarea name="content" ref={contentInput} value={state.content} onChange={handlerChangeState} /> </div> <div> <span>오늘의 감정점수 : </span> <select name="emotion" value={state.emotion} onChange={handlerChangeState}> <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> <option value={6}>6</option> </select> </div> <div> <button onClick={handleSubmit}>일기 저장하기</button> </div> </div>); }; export default DiaryEditor;
DiaryList.js
import DiaryItem from "./DiaryItem"; const DiaryList = ({ diaryList }) => { return ( <div className="DiaryList"> <h2>일기 리스트</h2> <h4>{diaryList.length}개의 일기가 있습니다.</h4> <div> {diaryList.map((it) => ( <DiaryItem key={it.id} {...it} /> )) } </div> </div> ) } DiaryList.defaultProps = { diaryList: [], } export default DiaryList;
DiaryItem.js
const DiaryItem = ({ author, content, emotion, create_date }) => { return ( <div className="DiaryItem"> <div className="info"> <span> 작성자 : {author} | 감정점수 : {emotion} </span> <br /> <span className="date"> {new Date(create_date).toLocaleString()}</span> </div> <div className="content">{content}</div> </div > ); }; export default DiaryItem;
39.React에서 배열 사용하기 2 - 데이터 추가하기
강의 :
App.js
import DiaryEditor from './DiaryEditor'; import DiaryList from './DiaryList'; import './App.css'; import { useState } from 'react'; import { useRef } from 'react'; // const dummyList = [ // { // id: 1, // author: "홍길동", // content: "하이1 ", // emotion: 2, // create_date: new Date().getTime() // }, // { // id: 2, // author: "이순신", // content: "하이2 ", // emotion: 3, // create_date: new Date().getTime() // }, // { // id: 3, // author: "테스터", // content: "하이3 ", // emotion: 4, // create_date: new Date().getTime() // } // ] function App() { const [data, setData] = useState([]); const dataId = useRef(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; setData([newItem, ...data]); } return ( <div className="App"> <DiaryEditor onCreate={onCreate} /> <DiaryList diaryList={data} /> </div> ); } export default App;
DiaryEditor.js
import { useRef, useState } from "react"; const DiaryEditor = ({ onCreate }) => { const authorInput = useRef(); const contentInput = useRef(); const [state, setState] = useState({ author: "", content: "", emotion: 3 }); const handlerChangeState = (e) => { // console.log(e.target.name, e.target.value); setState({ ...state, [e.target.name]: e.target.value }) } const handleSubmit = (e) => { if (state.author.length < 1) { //alert("작성자는 최소 1글자 이상 입력해주세요."); authorInput.current.focus(); return; } if (state.content.length < 5) { //alert("일기 본문은 최소 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 name="author" ref={authorInput} value={state.author} onChange={handlerChangeState} /> {/* onChange={(e) => { // setState({...state, author: e.target.value }) // */} </div> <div> <textarea name="content" ref={contentInput} value={state.content} onChange={handlerChangeState} /> </div> <div> <span>오늘의 감정점수 : </span> <select name="emotion" value={state.emotion} onChange={handlerChangeState}> <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> <option value={6}>6</option> </select> </div> <div> <button onClick={handleSubmit}>일기 저장하기</button> </div> </div>); }; export default DiaryEditor;
40.React에서 배열 사용하기 3 - 데이터 삭제하기
강의 :
App.js
const onDelete = (targetId) => { console.log(`${targetId} 가 삭제되었습니다.`); const newDiaryList = data.filter(item => item.id !== targetId); setData(newDiaryList); } <DiaryList diaryList={data} onDelete={onDelete} />
DiaryList.js
const DiaryList = ({ diaryList, onDelete }) => { return ( <div className="DiaryList"> <h2>일기 리스트</h2> <h4>{diaryList.length}개의 일기가 있습니다.</h4> <div> {diaryList.map((it) => ( <DiaryItem key={it.id} {...it} onDelete={onDelete} /> )) } </div> </div> ) }
DiaryItem.js
const DiaryItem = ({ id, author, content, emotion, create_date, onDelete }) => { return ( <div className="DiaryItem"> <div className="info"> <span> 작성자 : {author} | 감정점수 : {emotion} </span> <br /> <span className="date"> {new Date(create_date).toLocaleString()}</span> </div> <div className="content">{content}</div> <button onClick={() => { if (window.confirm(`${id} 번째 일기를 정말 삭제하시겠습니까?`)) { onDelete(id); } }}>삭제하기</button> </div > ); }; export default DiaryItem;
41.React에서 배열 사용하기 4 - 데이터 수정하기
강의 :
App.js
~ const onEdit = (targetId, newContent) => { setData( data.map((it) => it.id === targetId ? { ...it, content: newContent } : it )); } return ( <div className="App"> <DiaryEditor onCreate={onCreate} /> <DiaryList onEdit={onEdit} diaryList={data} onRemove={onRemove} /> </div> ); ~
DiaryList.js
import DiaryItem from "./DiaryItem"; const DiaryList = ({ diaryList, onRemove, onEdit }) => { return ( <div className="DiaryList"> <h2>일기 리스트</h2> <h4>{diaryList.length}개의 일기가 있습니다.</h4> <div> {diaryList.map((it) => ( <DiaryItem onEdit={onEdit} key={it.id} {...it} onRemove={onRemove} /> )) } </div> </div> ) } DiaryList.defaultProps = { diaryList: [], } export default DiaryList;
DiaryItem.js
import { useState } from 'react'; import { useRef } from 'react'; const DiaryItem = ({ id, author, content, emotion, create_date, onRemove, onEdit }) => { 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); setIsEdit(false); } } return ( <div className="DiaryItem"> <div className="info"> <span> 작성자 : {author} | 감정점수 : {emotion} </span> <br /> <span className="date"> {new Date(create_date).toLocaleString()}</span> </div> <div className="content"> {isEdit ? ( <> <textarea ref={localContentInput} value={localContent} onChange={(e) => setLocalContent(e.target.value)} ></textarea> </> ) : ( <>{content}</> )} </div> {isEdit ? (<> <button onClick={handleQuitEdit}>수정취소</button> <button onClick={handleEdit} >수정완료</button> </>) : (<> <button onClick={handleRemove}>삭제하기</button> <button onClick={toggleIsEdit}>수정하기</button> </>) } </div > ); }; export default DiaryItem;
42.React Lifecycle 제어하기 - useEffect
강의 :
UnmountTest
import React, { useEffect } from 'react'; import { useState } from 'react'; const UnmountTest = () => { useEffect(() => { console.log("Mount!"); return () => { //Unmount 시점에 실행되게 됨 console.log("Unmount!"); } }, []); return <div>Unmount Test Component</div> } const Lifiecycle = () => { const [isVisible, setIsVisible] = useState(false); const toggle = () => setIsVisible(!isVisible); // const [count, setCount] = useState(0); // const [text, setText] = useState(""); // useEffect(() => { // console.log("mount!"); // }); // useEffect(() => { // console.log(`count is update : ${count}`); // if (count > 5) { // alert("count 가 5를 넘었습니다.따라서 1로 초기화 합니다."); // setCount(1); // } // }, [count]); // useEffect(() => { // console.log(`text is update : ${text}`); // }, [text]); return ( <div style={{ padding: 20 }}> <button onClick={toggle}>ON/OFF</button> {isVisible && <UnmountTest />} </div> ); }; export default Lifiecycle;
43.React에서 API 호출하기
강의 :
App.js
import DiaryEditor from './DiaryEditor'; import DiaryList from './DiaryList'; import './App.css'; import { useState, useEffect } from 'react'; import { useRef } from 'react'; // const dummyList = [ // { // id: 1, // author: "홍길동", // content: "하이1 ", // emotion: 2, // create_date: new Date().getTime() // }, // { // id: 2, // author: "이순신", // content: "하이2 ", // emotion: 3, // create_date: new Date().getTime() // }, // { // id: 3, // author: "테스터", // content: "하이3 ", // emotion: 4, // create_date: new Date().getTime() // } // ] //http://jsonplaceholder.typicode.com/posts/1/comments function App() { const [data, setData] = useState([]); const dataId = useRef(0); const getData = async () => { const res = await fetch('http://jsonplaceholder.typicode.com/comments') .then(data => data.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, create_date: new Date().getTime(), id: dataId.current++ } }) setData(initData); } useEffect(() => { getData(); }, []) const onCreate = (author, content, emotion) => { const create_date = new Date().getTime(); const newItem = { author, content, emotion, create_date, id: dataId.current }; dataId.current += 1; setData([newItem, ...data]); } const onRemove = (targetId) => { console.log(`${targetId} 가 삭제되었습니다.`); const newDiaryList = data.filter(item => item.id !== targetId); setData(newDiaryList); } const onEdit = (targetId, newContent) => { setData( data.map((it) => it.id === targetId ? { ...it, content: newContent } : it )); } return ( <div className="App"> <DiaryEditor onCreate={onCreate} /> <DiaryList onEdit={onEdit} diaryList={data} onRemove={onRemove} /> </div> ); } export default App;
44.React developer tools
소스 :
react developer tools 다운로드 링크입니다.
45.최적화 1 - useMemo
소스 :
데이터 변하지 않으면 기존 메모값을 그대로 사용한다.
여기서는 data.length 데이터 길이값을 기준으로 설정 했다.
const getDiaryAnalisys = useMemo(() => { console.log("일기 분석 시작"); 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 } = getDiaryAnalisys; ~
46.최적화 2 - React.memo
강의 :
강의 진행 중 사용된 소스코드 입니다.
https://codesandbox.io/s/chapt-11-9ppqm
OptimizeTest.js
import React, { useState, useEffect } from 'react'; const TextView = React.memo(({ text }) => { useEffect(() => { console.log(`Update : : Text : ${text}`) }); return <div>{text}</div> }); const CountView = React.memo(({ count }) => { useEffect(() => { console.log(`Update : : count : ${count}`) }); return <div>{count}</div> }); const OptimizeTest = () => { const [count, setCount] = useState(1); const [text, setText] = useState(""); return ( <div style={{ padding: 50 }}> <div> <h2>count</h2> <CountView count={count} /> <button onClick={() => setCount(count + 1)}>+</button> </div> <div> <h2>text</h2> <TextView text={text} /> <input value={text} onChange={(e) => setText(e.target.value)} /> </div> </div > ); }; export default OptimizeTest;
객체일경우 깊은 복사
import React, { useState, useEffect } from 'react'; const CounterA = React.memo(({ count }) => { useEffect(() => { console.log(` CounterA update -- count :${count}`); }) return <div>{count}</div> }); const CounterB = React.memo(({ obj }) => { useEffect(() => { console.log(` CounterB update -- count :${obj.count}`); }) return <div>{obj.count}</div> }); const areEqual = (prevProps, nextProps) => { //return true //이전 프롭스 현재 프롭스가 같다 -> 리랜더링을 일이키지 않게 된다. //return flase // 이전 프롭스와 현재 프롭스가 다르면 -> 리랜더링을 일으킨다. return prevProps.obj.count === nextProps.obj.count; } const MemoizedCounterB = React.memo(CounterB, areEqual); const OptimizeTest = () => { const [count, setCount] = useState(1); const [obj, setObj] = useState({ count: 1 }); return ( <div style={{ padding: 50 }}> <div> <h2>Counter A</h2> <CounterA count={count} /> <button onClick={() => setCount(count)}>A button</button> </div> <div> <h2>Counter B</h2> <MemoizedCounterB obj={obj} /> <button onClick={() => setObj({ count: obj.count, })}>B button</button> </div> </div > ); }; export default OptimizeTest;
47.최적화 3 - useCallback
강의 :
DiaryEditor.js
const DiaryEditor = ({ onCreate }) => { ~ export default React.memo(DiaryEditor);
App.js
~ const onCreate = useCallback((author, content, emotion) => { console.log(" onCreate 합수 실행"); const create_date = new Date().getTime(); const newItem = { author, content, emotion, create_date, id: dataId.current }; dataId.current += 1; //setData([newItem, ...data]); => setData((data) => [newItem, ...data]); }, []); ~
참조 :
[React] 리액트 Hooks : useCallback() 함수 사용법
48.최적화 4 - 최적화 완성
강의 :
DiaryItem.js
~ export default React.memo(DiaryItem);
App.js
~ const onRemove = useCallback((targetId) => { // const newDiaryList = data.filter((it) => it.id !== 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 ) ); }, []); ~
49.복잡한 상태 관리 로직 분리하기 - useReducer
강의 :
App.js
import { useEffect, useMemo, useRef, useCallback, useReducer } from "react"; import "./App.css"; import DiaryEditor from "./DiaryEditor"; import DiaryList from "./DiaryList"; import OptimizeTest from "./OptimizeTest"; const reducer = (state, action) => { switch (action.type) { case "INIT": return action.data; case "CREATE": { const create_date = new Date().getTime(); const newItem = { ...action.data, create_date } return [newItem, ...state]; } case "REMOVE": return state.filter(item => item.id !== action.targetId) case "EDIT": return state.map(item => item.id === action.targetId ? { ...item, content: action.newContent } : item) default: return state; } } const App = () => { // const [data, setData] = useState([]); const dataId = useRef(0); const [data, dispatch] = useReducer(reducer, []); 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++ }; }); dispatch({ type: "INIT", data: initData }); }; useEffect(() => { setTimeout(() => { getData(); }, 1500); }, []); const onCreate = (author, content, emotion) => { dispatch({ type: "CREATE", data: { author, content, emotion, id: dataId.current } }) dataId.current += 1; }; const onRemove = useCallback((targetId) => { dispatch({ type: "REMOVE", targetId }) }, []); const onEdit = useCallback((targetId, newContent) => { dispatch({ type: "EDIT", targetId, newContent }) }, []); const getDiaryAnalysis = useMemo(() => { if (data.length === 0) { return { goodcount: 0, badCount: 0, goodRatio: 0 }; } console.log("일기 분석 시작"); const goodCount = data.filter((it) => it.emotion >= 3).length; const badCount = data.length - goodCount; const goodRatio = (goodCount / data.length) * 100.0; return { goodCount, badCount, goodRatio }; }, [data.length]); const { goodCount, badCount, goodRatio } = getDiaryAnalysis; return ( <div className="App"> <OptimizeTest /> <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;
50.컴포넌트 트리에 데이터 공급하기 - Context
강의 :
App.js
export const DiaryStateContext = React.createContext(); export const DiaryDispatchContext = React.createContext(); ~ const memorizedDispatches = useMemo(() => { return { onCreate, onRemove, onEdit } }, []) ~ return ( <DiaryStateContext.Provider value={data} > <DiaryDispatchContext.Provider value={memorizedDispatches}> <div className="App"> <OptimizeTest /> <DiaryEditor onCreate={onCreate} /> <div>전체 일기 : {data.length}</div> <div>기분 좋은 일기 개수 : {goodCount}</div> <div>기분 나쁜 일기 개수 : {badCount}</div> <div>기분 좋은 일기 비율 : {goodRatio}</div> <DiaryList /> </div> </DiaryDispatchContext.Provider> </DiaryStateContext.Provider> ); ~
DiaryList.js
import React, { useContext } from 'react'; import DiaryItem from "./DiaryItem"; import { DiaryStateContext } from "./App"; const DiaryList = ({ onRemove, onEdit }) => { const diaryList = useContext(DiaryStateContext); ~
DiaryEditor.js
import React, { useRef, useState, useEffect, useContext } from "react"; import { DiaryDispatchContext } from "./App"; const DiaryEditor = () => { const { onCreate } = useContext(DiaryDispatchContext) ~
DiaryItem.js
import React, { useState, useRef, useContext } from 'react'; import { DiaryDispatchContext } from './App'; const DiaryItem = ({ id, author, content, emotion, create_date }) => { const { onRemove, onEdit } = useContext(DiaryDispatchContext); ~
★ Redux store 사용법과 비교해 볼것
https://github.dev/braverokmc79/performance-exhibition/tree/main/client
1)index.js
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './components/App'; import { Provider } from 'react-redux'; import { applyMiddleware, legacy_createStore as createStore } from 'redux'; import promiseMiddleware from 'redux-promise'; import ReduxThunk from 'redux-thunk'; import Reducer from './_reducers'; const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, ReduxThunk)(createStore); const devTools = window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(); const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Provider store={createStoreWithMiddleware(Reducer, devTools)}> < App /> </Provider > );
2) _actions/types.js
export const LOGIN_USER = 'login_user'; export const LOGIN_KAKAO_USER = 'login_kakao_user'; export const LOGIN_NAVER_USER = 'login_naver_user'; export const REGISTER_USER = 'register_user'; export const AUTH_USER = 'auth_user'; export const LOGOUT_USER = 'logout_user';
3) _actions/user_actions.js
import axios from 'axios'; import { LOGIN_USER, LOGIN_KAKAO_USER, LOGIN_NAVER_USER, REGISTER_USER, AUTH_USER, LOGOUT_USER } from './types'; import { USER_SERVER } from '../components/Config.js'; /** 유저 로그인 */ export function loginUser(dataTomSubmit) { const request = axios.post(`${USER_SERVER}/login`, dataTomSubmit) .then((res) => { return res.data; }).catch((Error) => { console.error("에러 :", Error); }); return { type: LOGIN_USER, payload: request } } /** 카카오 유저 로그인 */ export function loginUserKakao(dataTomSubmit) { const request = axios.post(`${USER_SERVER}/login/kakao`, dataTomSubmit) .then((res) => { return res.data; }).catch((Error) => { console.error("에러 :", Error); }); return { type: LOGIN_KAKAO_USER, payload: request } } /** 네이버 유저 로그인 */ export function loginUserNaver(dataTomSubmit) { const request = axios.post(`${USER_SERVER}/login/naver`, dataTomSubmit) .then((res) => { return res.data; }).catch((Error) => { console.error("에러 :", Error); }); return { type: LOGIN_NAVER_USER, payload: request } } /** 유저 등록 */ export function registerUser(dataTomSubmit) { const request = axios.post(`${USER_SERVER}/register`, dataTomSubmit) .then(res => res.data); return { type: REGISTER_USER, payload: request } } /** 유저 권한확인 */ export function auth() { const request = axios.post(`${USER_SERVER}/auth`) .then(res => res.data); return { type: AUTH_USER, payload: request } } /** 로그아웃 */ export function logoutUser() { const request = axios.get(`${USER_SERVER}/logout`) .then(response => response.data); return { type: LOGOUT_USER, payload: request } }
4) _reducers/index.js
import { combineReducers } from 'redux'; import user from './user_reducer'; const rootReducer = combineReducers({ user, }); export default rootReducer;
5) _reducers/user_reducer.js
import { LOGIN_USER, LOGIN_KAKAO_USER, LOGIN_NAVER_USER, REGISTER_USER, AUTH_USER, LOGOUT_USER } from '../_actions/types'; export default function user_reducers(state = {}, action) { switch (action.type) { case LOGIN_USER: return { ...state, loginSuccess: action.payload } //카카오 로그인 case LOGIN_KAKAO_USER: return { ...state, loginSuccess: action.payload } //네이버 로그인 case LOGIN_NAVER_USER: return { ...state, loginSuccess: action.payload } case REGISTER_USER: return { ...state, register: action.payload } case AUTH_USER: return { ...state, userData: action.payload } case LOGOUT_USER: return { ...state } default: return state; } }
6) KakaoLoginHandler.js
import React, { useEffect } from "react"; import { useDispatch } from "react-redux"; ~ const KakaoLoginHandler = () => { const dispatch = useDispatch(); ~ ~ //4. nodejs 서버로로 데이터를 넘겨준다 dispatch(loginUserKakao(dataToSubmit)) .then(response => { if (response.payload.loginSuccess) { window.localStorage.setItem('userId', response.payload.userId); console.log("loginSuccess :", response.payload); naigate("/"); } else { console.log("로그인 실패 :"); } }) .catch(err => { console.log("에러 :", err); }); ~
댓글 ( 4)
댓글 남기기