Redux Toolkit
yarn add react-redux @reduxjs/toolkit
DevTool
redux dev tools 라는 기능이 있다. 디버깅 할 때 좋다.
구글 웹 스토어에서 다운 받는 기능이다.
https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=ko
우리가 toolKit 다운 받을 때 이미 devTools도 다운받았다
= toolKit 안 깔면 devTools 설정 따로 해줘야한다.
thunk
immer
JSON 서버
프론트 엔드 만들 때 임시로 서버 역할을 하는 것
mock 데이터를 가지고 있다.
yarn add json-server
백 엔드 서버이기 때문에 리액트와 별개로 켜줘야 한다.
yarn json-server --watch db.json --port 3001
항상 이 서버에 접근하고 싶다면 리액트 프로젝트를 배포하는 것 처럼 배포해야한다.
axios
브라우저와 node.js를 위한 promise 기반 HTTP 클라이언트 라이브러리..?
- HTTP를 이용해서 서버와 통신하기 위해 사용하는 패키지
api 서버와 통신하는 방법을 필수로 알아야 한다.
axios method 4가지
yarn add axios
GET - read POST - create
DELETE -delete
PATCH -update
요청하는 법은 라이브러리에서 정해준 대로 따라야 한다.
api 명세를 작성하는 법
path variable
정해져 있는 아이디 같은 것들을 사용해서 넘겨주는 경우
예시
GET /posts/1
query
키값이 있는 경우
예시
GET / posts?title=joson-server&author=typicode
await async를 써주었다. 이건 promise 객체에 쓰는 것이다. axios 로 만든 json 데이터를 받을 때 promise 객체인가 보다.
보통 백 엔드에서 post는 서버에 새로운 데이터를 넣어줄 때 쓰지만
여기서는 클라이언트의 데이터를 body 형태로 서버로 보내줄 때 쓴다.
네트워크의 헤더 텝
네트워크의 페이로드 탭 - 우리가 보낸 페이로드가 있는 경우만 뜬다.
네트워크의 응답 탭 - 우리의 요청에 대한 서버의 응답
json서버는 post 요청을 보냈을 때 클라이언트가 보낸 body를 그대로 응답해주도록 되어있다. (자동이 아니라 api 명세서에 설정되어 있는 내용)
patch: 기존 데이터를 요청하는 부분만 수정할 때
{
"todos": [
{
"id": 1,
"title": "json-server", => "title" : "a"
"content": "json-server를 배워봅시다."
}
]
}
put: 기존 테이터를 모두 수정할 때
{
"todos": [
{
"id": 1, => "id" : null
"title": "json-server", => "title" : "a"
"content": "json-server를 배워봅시다." => "content" : null
}
]
}
사실 post를 통해서도 수정 기능을 만들 수 있지만 put과 patch를 쓰자고 HTTP환경에서 서로 약속한 것
axios get, post, patch, delete 이용하여 CRUD 구현한 app.js 코드
// src/App.js
import React, { useEffect, useState } from "react";
import axios from "axios"; // axios import 합니다.
const App = () => {
const [todos, setTodos] = useState(null);
const [todo, setTodo] = useState({
id: "",
title: "",
content: "",
});
// axios를 통해서 get 요청을 하는 함수를 생성합니다.
// 비동기처리를 해야하므로 async/await 구문을 통해서 처리합니다.
const fetchTodos = async () => {
const { data } = await axios.get("<http://localhost:3001/todos>");
setTodos(data); // 서버로부터 fetching한 데이터를 useState의 state로 set 합니다.
};
// 생성한 함수를 컴포넌트가 mount 됐을 떄 실행하기 위해 useEffect를 사용합니다.
useEffect(() => {
// effect 구문에 생성한 함수를 넣어 실행합니다.
fetchTodos();
}, []);
const handleOnSubmitAddTodo = async (todo) => {
await axios.post("<http://localhost:3001/todos>", todo);
const newTodo = {
id: todos.length + 1,
title: todo.title,
content: todo.content,
};
setTodos([...todos, newTodo]);
};
const handleOnClickDeleteTodo = async (id) => {
await axios.delete(`http://localhost:3001/todos/${id}`);
setTodos(todos.filter((item) => item.id !== id));
};
const handleOnClickUpdateTodo = async (id, newTodo) => {
await axios.patch(`http://localhost:3001/todos/${id}`, newTodo);
const newTodoList = todos.map((item) =>
item.id === id
? { ...todo, title: newTodo.title, content: newTodo.content }
: item
);
setTodos(newTodoList);
};
// data fetching이 정상적으로 되었는지 콘솔을 통해 확인합니다.
console.log(todos); // App.js:16
return (
<form
onSubmit={(e) => {
e.preventDefault();
handleOnSubmitAddTodo(todo);
}}
>
<label htmlFor="title">제목</label>
<input
type="text"
onChange={(e) => {
const { name, value } = e.target;
setTodo({ ...todo, [name]: value });
}}
id="title"
name="title"
/>
<label htmlFor="content">내용</label>
<input
type="text"
onChange={(e) => {
const { name, value } = e.target;
setTodo({ ...todo, [name]: value });
}}
id="content"
name="content"
/>
<button>추가</button>
<div>
{todos?.map((item) => {
return (
<div key={item.id}>
<div>제목: {item.title}</div>
<div>내용: {item.content}</div>
<button
type="button"
onClick={() => handleOnClickDeleteTodo(item.id)}
>
삭제
</button>
<button
type="button"
onClick={() => handleOnClickUpdateTodo(item.id, todo)}
>
수정
</button>
<input
type="text"
placeholder="제목 수정 값 입력"
onChange={(e) => {
const { name, value } = e.target;
setTodo({ ...todo, [name]: value });
}}
id="edit"
name="title"
/>
<input
type="text"
placeholder="내용 수정 값 입력"
onChange={(e) => {
const { name, value } = e.target;
setTodo({ ...todo, [name]: value });
}}
id="edit"
name="content"
/>
</div>
);
})}
</div>
</form>
);
};
export default App;
redux 미들웨어
액션⇒ 미들웨어⇒리듀서⇒스토어
ex) 카운터에서 버튼누르면 3초 있다가 +1하게 하려면
3초 있다가라는 설정은 미들웨어에서 해준다. = 비동기처리가 필요할 때 미들웨어를 이용한다.
미들웨어 중 가장 많이 사용되고 있는 것이 redux-thunk이다.
원리
dispatch 할 때 원래를 객체를 넣었는데 대신에 함수를 넣어주어 함수를 실행하고 함수 안에서 객체를 실행하게 하는 것이다.
dispatch(함수) ⇒ 함수 실행 ⇒ 함수 안에서 dispatch(객체)
사용법
모듈 안에서 toolkit의 createAsyncthunk 이라는 api를 가져와서 쓰면 된다.
couterSlice.js
export const _addNumber = createAsyncThunk(
//첫번째 인자: action value
"addNumber",
//두번째 인자 : 콜백함수
(payload, thunkAPI) => {
setTimeout(() => {
thunkAPI.dispatch(addNumber(payload));
}, 3000);
}
);
기존 액션 크리에이터 말고 thunk를 적용한 새 액션 크리에이터 넣어준다.
**const handleOnClickPlusNum = (payload) => {
dispatch(_addNumber(+payload));
};**
Thunk에서 Promise 객체를 다루는 법 + redux를 이용해서 state 관리
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
const initialState = {
todos: [],
isLoading: false,
error: null,
};
// // 액션 객체를 dispatch 해주는 api fulfillvalue, rejectwithvalue를 포함하는 Thunk 함수
export const __getTodos = createAsyncThunk(
"todos/getTodos",
async (payload, thunkAPI) => {
try {
const data = await axios.get("<http://localhost:3001/todos>");
console.log(data);
return thunkAPI.fulfillWithValue(data.data);
} catch (error) {
console.log(error);
return thunkAPI.rejectWithValue(error);
}
}
);
const todoSlice = createSlice({
name: "todo",
initialState,
reducers: {},
extraReducers: {
[__getTodos.pending]: (state) => {
state.isLoading = true;
},
[__getTodos.fulfilled]: (state, action) => {
state.isLoading = false;
state.todos = action.payload;
},
[__getTodos.rejected]: (state, action) => {
state.isLoading = false;
state.error = action.payload;
},
},
});
export const {} = todoSlice.actions;
export default todoSlice.reducer;
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { __getTodos } from "../modules/todoSlice";
const Todos = () => {
const dispatch = useDispatch();
const { isLoading, error, todos } = useSelector((state) => state.todo);
console.log(isLoading, error, todos);
useEffect(() => {
dispatch(__getTodos());
}, [dispatch]);
if (isLoading) {
return <div>로딩 중</div>;
}
if (error) {
return <div>{error.message}</div>;
}
return todos.map((item) => {
return (
<div key={item.id}>
<div>{item.title}</div>
<div>{item.content}</div>
</div>
);
});
};
export default Todos;
memo(hook은 아니고 알아야할 개념), useMemo, useCallback
memo:
컴포넌트에 불필요한 렌더링을 하지 않도록 하는 함수
불필요한 렌더링 == 화면에서 변경이 없는데도 리렌더링되는 경우
리렌더링 조건 (복습)
- 부모 컴포넌트 렌더링
- 컴포넌트의 state 변경
- 부모로부터 전달받는 props 값이 변경된 경우
불필요한 렌더링을 줄이면 프로젝트의 부하를 줄이고 퍼포먼스 능력을 향상 시킬 수 있다.
부모 컴포넌트가 리렌더링이 되더라도 자식 컴포넌트가 리렌더링 되지 않게 하는 처리
import {memo} from “react”
const Button = () => {
return <div>
<button>버튼</button>
</div>}
export default memo(Button);
useCallback vs useMemo
useCallback : 프롭스로 넘겨주는 함수는 선언된 장소인 컴포넌트가 리렌더링 될 때 재 생성 되는데, useCallback을 써주면 재 생성되지 않는다.
import {useCallback} from "react";
const onClickHandler = useCallback(() ⇒ {
console.log("hellobutton")
},[]);
useMemo : 프롭스로 넘겨주는 객체가 위치한 장소의 컴포넌트가 리렌더링 될 때 객체도 재생성 되는데, useMemo를 써주면 재 생성되지 않는다.
useCallback과 같은 데 배열이나, 객체의 재 생성을 막아 줄 때 쓴다.
import {useMemo} from "react";
const data = useMemo(()=>{
return [
{
id:1,
title:"react",
},
]},[]);
원시 데이터: let a= 1 이런 식의 데이터들은 useMemo를 안 써도 리렌더링 하지 않는다.
위의 최적화 hook을 남용하면 안되는 이유
- 렌더링 전 후의 값을 비교해서 같지 않으면 재생성하고 같으면 재생성을 하지 않는데 ? (강사님이 내가 생각한 것과 반대로 얘기하시는데 뭘 비교하는 거지) 이러한 비교 과정이 불필요하게 들어갈 수 있다.
Custom Hook
반복되는 로직이나 기능을 우리가 Custom Hook으로 만들어서 관리 할 수 있다.
useInput이라는 커스텀 훅을 만들어보자 - 이름은 마음대로 지어되나, 앞에 use를 붙여야한다.
Redux Toolkit
yarn add react-redux @reduxjs/toolkit
DevTool
redux dev tools 라는 기능이 있다. 디버깅 할 때 좋다.
구글 웹 스토어에서 다운 받는 기능이다.
https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=ko
우리가 toolKit 다운 받을 때 이미 devTools도 다운받았다
= toolKit 안 깔면 devTools 설정 따로 해줘야한다.
thunk
immer
JSON 서버
프론트 엔드 만들 때 임시로 서버 역할을 하는 것
mock 데이터를 가지고 있다.
yarn add json-server
백 엔드 서버이기 때문에 리액트와 별개로 켜줘야 한다.
yarn json-server --watch db.json --port 3001
항상 이 서버에 접근하고 싶다면 리액트 프로젝트를 배포하는 것 처럼 배포해야한다.
axios
브라우저와 node.js를 위한 promise 기반 HTTP 클라이언트 라이브러리..?
- HTTP를 이용해서 서버와 통신하기 위해 사용하는 패키지
api 서버와 통신하는 방법을 필수로 알아야 한다.
axios method 4가지
yarn add axios
GET - read POST - create
DELETE -delete
PATCH -update
요청하는 법은 라이브러리에서 정해준 대로 따라야 한다.
api 명세를 작성하는 법
path variable
정해져 있는 아이디 같은 것들을 사용해서 넘겨주는 경우
예시
GET /posts/1
query
키값이 있는 경우
예시
GET / posts?title=joson-server&author=typicode
await async를 써주었다. 이건 promise 객체에 쓰는 것이다. axios 로 만든 json 데이터를 받을 때 promise 객체인가 보다.
보통 백 엔드에서 post는 서버에 새로운 데이터를 넣어줄 때 쓰지만
여기서는 클라이언트의 데이터를 body 형태로 서버로 보내줄 때 쓴다.
네트워크의 헤더 텝
네트워크의 페이로드 탭 - 우리가 보낸 페이로드가 있는 경우만 뜬다.
네트워크의 응답 탭 - 우리의 요청에 대한 서버의 응답
json서버는 post 요청을 보냈을 때 클라이언트가 보낸 body를 그대로 응답해주도록 되어있다. (자동이 아니라 api 명세서에 설정되어 있는 내용)
patch: 기존 데이터를 요청하는 부분만 수정할 때
{
"todos": [
{
"id": 1,
"title": "json-server", => "title" : "a"
"content": "json-server를 배워봅시다."
}
]
}
put: 기존 테이터를 모두 수정할 때
{
"todos": [
{
"id": 1, => "id" : null
"title": "json-server", => "title" : "a"
"content": "json-server를 배워봅시다." => "content" : null
}
]
}
사실 post를 통해서도 수정 기능을 만들 수 있지만 put과 patch를 쓰자고 HTTP환경에서 서로 약속한 것
axios get, post, patch, delete 이용하여 CRUD 구현한 app.js 코드
// src/App.js
import React, { useEffect, useState } from "react";
import axios from "axios"; // axios import 합니다.
const App = () => {
const [todos, setTodos] = useState(null);
const [todo, setTodo] = useState({
id: "",
title: "",
content: "",
});
// axios를 통해서 get 요청을 하는 함수를 생성합니다.
// 비동기처리를 해야하므로 async/await 구문을 통해서 처리합니다.
const fetchTodos = async () => {
const { data } = await axios.get("<http://localhost:3001/todos>");
setTodos(data); // 서버로부터 fetching한 데이터를 useState의 state로 set 합니다.
};
// 생성한 함수를 컴포넌트가 mount 됐을 떄 실행하기 위해 useEffect를 사용합니다.
useEffect(() => {
// effect 구문에 생성한 함수를 넣어 실행합니다.
fetchTodos();
}, []);
const handleOnSubmitAddTodo = async (todo) => {
await axios.post("<http://localhost:3001/todos>", todo);
const newTodo = {
id: todos.length + 1,
title: todo.title,
content: todo.content,
};
setTodos([...todos, newTodo]);
};
const handleOnClickDeleteTodo = async (id) => {
await axios.delete(`http://localhost:3001/todos/${id}`);
setTodos(todos.filter((item) => item.id !== id));
};
const handleOnClickUpdateTodo = async (id, newTodo) => {
await axios.patch(`http://localhost:3001/todos/${id}`, newTodo);
const newTodoList = todos.map((item) =>
item.id === id
? { ...todo, title: newTodo.title, content: newTodo.content }
: item
);
setTodos(newTodoList);
};
// data fetching이 정상적으로 되었는지 콘솔을 통해 확인합니다.
console.log(todos); // App.js:16
return (
<form
onSubmit={(e) => {
e.preventDefault();
handleOnSubmitAddTodo(todo);
}}
>
<label htmlFor="title">제목</label>
<input
type="text"
onChange={(e) => {
const { name, value } = e.target;
setTodo({ ...todo, [name]: value });
}}
id="title"
name="title"
/>
<label htmlFor="content">내용</label>
<input
type="text"
onChange={(e) => {
const { name, value } = e.target;
setTodo({ ...todo, [name]: value });
}}
id="content"
name="content"
/>
<button>추가</button>
<div>
{todos?.map((item) => {
return (
<div key={item.id}>
<div>제목: {item.title}</div>
<div>내용: {item.content}</div>
<button
type="button"
onClick={() => handleOnClickDeleteTodo(item.id)}
>
삭제
</button>
<button
type="button"
onClick={() => handleOnClickUpdateTodo(item.id, todo)}
>
수정
</button>
<input
type="text"
placeholder="제목 수정 값 입력"
onChange={(e) => {
const { name, value } = e.target;
setTodo({ ...todo, [name]: value });
}}
id="edit"
name="title"
/>
<input
type="text"
placeholder="내용 수정 값 입력"
onChange={(e) => {
const { name, value } = e.target;
setTodo({ ...todo, [name]: value });
}}
id="edit"
name="content"
/>
</div>
);
})}
</div>
</form>
);
};
export default App;
redux 미들웨어
액션⇒ 미들웨어⇒리듀서⇒스토어
ex) 카운터에서 버튼누르면 3초 있다가 +1하게 하려면
3초 있다가라는 설정은 미들웨어에서 해준다. = 비동기처리가 필요할 때 미들웨어를 이용한다.
미들웨어 중 가장 많이 사용되고 있는 것이 redux-thunk이다.
원리
dispatch 할 때 원래를 객체를 넣었는데 대신에 함수를 넣어주어 함수를 실행하고 함수 안에서 객체를 실행하게 하는 것이다.
dispatch(함수) ⇒ 함수 실행 ⇒ 함수 안에서 dispatch(객체)
사용법
모듈 안에서 toolkit의 createAsyncthunk 이라는 api를 가져와서 쓰면 된다.
couterSlice.js
export const _addNumber = createAsyncThunk(
//첫번째 인자: action value
"addNumber",
//두번째 인자 : 콜백함수
(payload, thunkAPI) => {
setTimeout(() => {
thunkAPI.dispatch(addNumber(payload));
}, 3000);
}
);
기존 액션 크리에이터 말고 thunk를 적용한 새 액션 크리에이터 넣어준다.
**const handleOnClickPlusNum = (payload) => {
dispatch(_addNumber(+payload));
};**
Thunk에서 Promise 객체를 다루는 법 + redux를 이용해서 state 관리
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
const initialState = {
todos: [],
isLoading: false,
error: null,
};
// // 액션 객체를 dispatch 해주는 api fulfillvalue, rejectwithvalue를 포함하는 Thunk 함수
export const __getTodos = createAsyncThunk(
"todos/getTodos",
async (payload, thunkAPI) => {
try {
const data = await axios.get("<http://localhost:3001/todos>");
console.log(data);
return thunkAPI.fulfillWithValue(data.data);
} catch (error) {
console.log(error);
return thunkAPI.rejectWithValue(error);
}
}
);
const todoSlice = createSlice({
name: "todo",
initialState,
reducers: {},
extraReducers: {
[__getTodos.pending]: (state) => {
state.isLoading = true;
},
[__getTodos.fulfilled]: (state, action) => {
state.isLoading = false;
state.todos = action.payload;
},
[__getTodos.rejected]: (state, action) => {
state.isLoading = false;
state.error = action.payload;
},
},
});
export const {} = todoSlice.actions;
export default todoSlice.reducer;
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { __getTodos } from "../modules/todoSlice";
const Todos = () => {
const dispatch = useDispatch();
const { isLoading, error, todos } = useSelector((state) => state.todo);
console.log(isLoading, error, todos);
useEffect(() => {
dispatch(__getTodos());
}, [dispatch]);
if (isLoading) {
return <div>로딩 중</div>;
}
if (error) {
return <div>{error.message}</div>;
}
return todos.map((item) => {
return (
<div key={item.id}>
<div>{item.title}</div>
<div>{item.content}</div>
</div>
);
});
};
export default Todos;
memo(hook은 아니고 알아야할 개념), useMemo, useCallback
memo:
컴포넌트에 불필요한 렌더링을 하지 않도록 하는 함수
불필요한 렌더링 == 화면에서 변경이 없는데도 리렌더링되는 경우
리렌더링 조건 (복습)
- 부모 컴포넌트 렌더링
- 컴포넌트의 state 변경
- 부모로부터 전달받는 props 값이 변경된 경우
불필요한 렌더링을 줄이면 프로젝트의 부하를 줄이고 퍼포먼스 능력을 향상 시킬 수 있다.
부모 컴포넌트가 리렌더링이 되더라도 자식 컴포넌트가 리렌더링 되지 않게 하는 처리
import {memo} from “react”
const Button = () => {
return <div>
<button>버튼</button>
</div>}
export default memo(Button);
useCallback vs useMemo
useCallback : 프롭스로 넘겨주는 함수는 선언된 장소인 컴포넌트가 리렌더링 될 때 재 생성 되는데, useCallback을 써주면 재 생성되지 않는다.
import {useCallback} from "react";
const onClickHandler = useCallback(() ⇒ {
console.log("hellobutton")
},[]);
useMemo : 프롭스로 넘겨주는 객체가 위치한 장소의 컴포넌트가 리렌더링 될 때 객체도 재생성 되는데, useMemo를 써주면 재 생성되지 않는다.
useCallback과 같은 데 배열이나, 객체의 재 생성을 막아 줄 때 쓴다.
import {useMemo} from "react";
const data = useMemo(()=>{
return [
{
id:1,
title:"react",
},
]},[]);
원시 데이터: let a= 1 이런 식의 데이터들은 useMemo를 안 써도 리렌더링 하지 않는다.
위의 최적화 hook을 남용하면 안되는 이유
- 렌더링 전 후의 값을 비교해서 같지 않으면 재생성하고 같으면 재생성을 하지 않는데 ? (강사님이 내가 생각한 것과 반대로 얘기하시는데 뭘 비교하는 거지) 이러한 비교 과정이 불필요하게 들어갈 수 있다.
Custom Hook
반복되는 로직이나 기능을 우리가 Custom Hook으로 만들어서 관리 할 수 있다.
useInput이라는 커스텀 훅을 만들어보자 - 이름은 마음대로 지어되나, 앞에 use를 붙여야한다.
Redux Toolkit
yarn add react-redux @reduxjs/toolkit
DevTool
redux dev tools 라는 기능이 있다. 디버깅 할 때 좋다.
구글 웹 스토어에서 다운 받는 기능이다.
https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=ko
우리가 toolKit 다운 받을 때 이미 devTools도 다운받았다
= toolKit 안 깔면 devTools 설정 따로 해줘야한다.
thunk
immer
JSON 서버
프론트 엔드 만들 때 임시로 서버 역할을 하는 것
mock 데이터를 가지고 있다.
yarn add json-server
백 엔드 서버이기 때문에 리액트와 별개로 켜줘야 한다.
yarn json-server --watch db.json --port 3001
항상 이 서버에 접근하고 싶다면 리액트 프로젝트를 배포하는 것 처럼 배포해야한다.
axios
브라우저와 node.js를 위한 promise 기반 HTTP 클라이언트 라이브러리..?
- HTTP를 이용해서 서버와 통신하기 위해 사용하는 패키지
api 서버와 통신하는 방법을 필수로 알아야 한다.
axios method 4가지
yarn add axios
GET - read POST - create
DELETE -delete
PATCH -update
요청하는 법은 라이브러리에서 정해준 대로 따라야 한다.
api 명세를 작성하는 법
path variable
정해져 있는 아이디 같은 것들을 사용해서 넘겨주는 경우
예시
GET /posts/1
query
키값이 있는 경우
예시
GET / posts?title=joson-server&author=typicode
await async를 써주었다. 이건 promise 객체에 쓰는 것이다. axios 로 만든 json 데이터를 받을 때 promise 객체인가 보다.
보통 백 엔드에서 post는 서버에 새로운 데이터를 넣어줄 때 쓰지만
여기서는 클라이언트의 데이터를 body 형태로 서버로 보내줄 때 쓴다.
네트워크의 헤더 텝
네트워크의 페이로드 탭 - 우리가 보낸 페이로드가 있는 경우만 뜬다.
네트워크의 응답 탭 - 우리의 요청에 대한 서버의 응답
json서버는 post 요청을 보냈을 때 클라이언트가 보낸 body를 그대로 응답해주도록 되어있다. (자동이 아니라 api 명세서에 설정되어 있는 내용)
patch: 기존 데이터를 요청하는 부분만 수정할 때
{
"todos": [
{
"id": 1,
"title": "json-server", => "title" : "a"
"content": "json-server를 배워봅시다."
}
]
}
put: 기존 테이터를 모두 수정할 때
{
"todos": [
{
"id": 1, => "id" : null
"title": "json-server", => "title" : "a"
"content": "json-server를 배워봅시다." => "content" : null
}
]
}
사실 post를 통해서도 수정 기능을 만들 수 있지만 put과 patch를 쓰자고 HTTP환경에서 서로 약속한 것
axios get, post, patch, delete 이용하여 CRUD 구현한 app.js 코드
// src/App.js
import React, { useEffect, useState } from "react";
import axios from "axios"; // axios import 합니다.
const App = () => {
const [todos, setTodos] = useState(null);
const [todo, setTodo] = useState({
id: "",
title: "",
content: "",
});
// axios를 통해서 get 요청을 하는 함수를 생성합니다.
// 비동기처리를 해야하므로 async/await 구문을 통해서 처리합니다.
const fetchTodos = async () => {
const { data } = await axios.get("<http://localhost:3001/todos>");
setTodos(data); // 서버로부터 fetching한 데이터를 useState의 state로 set 합니다.
};
// 생성한 함수를 컴포넌트가 mount 됐을 떄 실행하기 위해 useEffect를 사용합니다.
useEffect(() => {
// effect 구문에 생성한 함수를 넣어 실행합니다.
fetchTodos();
}, []);
const handleOnSubmitAddTodo = async (todo) => {
await axios.post("<http://localhost:3001/todos>", todo);
const newTodo = {
id: todos.length + 1,
title: todo.title,
content: todo.content,
};
setTodos([...todos, newTodo]);
};
const handleOnClickDeleteTodo = async (id) => {
await axios.delete(`http://localhost:3001/todos/${id}`);
setTodos(todos.filter((item) => item.id !== id));
};
const handleOnClickUpdateTodo = async (id, newTodo) => {
await axios.patch(`http://localhost:3001/todos/${id}`, newTodo);
const newTodoList = todos.map((item) =>
item.id === id
? { ...todo, title: newTodo.title, content: newTodo.content }
: item
);
setTodos(newTodoList);
};
// data fetching이 정상적으로 되었는지 콘솔을 통해 확인합니다.
console.log(todos); // App.js:16
return (
<form
onSubmit={(e) => {
e.preventDefault();
handleOnSubmitAddTodo(todo);
}}
>
<label htmlFor="title">제목</label>
<input
type="text"
onChange={(e) => {
const { name, value } = e.target;
setTodo({ ...todo, [name]: value });
}}
id="title"
name="title"
/>
<label htmlFor="content">내용</label>
<input
type="text"
onChange={(e) => {
const { name, value } = e.target;
setTodo({ ...todo, [name]: value });
}}
id="content"
name="content"
/>
<button>추가</button>
<div>
{todos?.map((item) => {
return (
<div key={item.id}>
<div>제목: {item.title}</div>
<div>내용: {item.content}</div>
<button
type="button"
onClick={() => handleOnClickDeleteTodo(item.id)}
>
삭제
</button>
<button
type="button"
onClick={() => handleOnClickUpdateTodo(item.id, todo)}
>
수정
</button>
<input
type="text"
placeholder="제목 수정 값 입력"
onChange={(e) => {
const { name, value } = e.target;
setTodo({ ...todo, [name]: value });
}}
id="edit"
name="title"
/>
<input
type="text"
placeholder="내용 수정 값 입력"
onChange={(e) => {
const { name, value } = e.target;
setTodo({ ...todo, [name]: value });
}}
id="edit"
name="content"
/>
</div>
);
})}
</div>
</form>
);
};
export default App;
redux 미들웨어
액션⇒ 미들웨어⇒리듀서⇒스토어
ex) 카운터에서 버튼누르면 3초 있다가 +1하게 하려면
3초 있다가라는 설정은 미들웨어에서 해준다. = 비동기처리가 필요할 때 미들웨어를 이용한다.
미들웨어 중 가장 많이 사용되고 있는 것이 redux-thunk이다.
원리
dispatch 할 때 원래를 객체를 넣었는데 대신에 함수를 넣어주어 함수를 실행하고 함수 안에서 객체를 실행하게 하는 것이다.
dispatch(함수) ⇒ 함수 실행 ⇒ 함수 안에서 dispatch(객체)
사용법
모듈 안에서 toolkit의 createAsyncthunk 이라는 api를 가져와서 쓰면 된다.
couterSlice.js
export const _addNumber = createAsyncThunk(
//첫번째 인자: action value
"addNumber",
//두번째 인자 : 콜백함수
(payload, thunkAPI) => {
setTimeout(() => {
thunkAPI.dispatch(addNumber(payload));
}, 3000);
}
);
기존 액션 크리에이터 말고 thunk를 적용한 새 액션 크리에이터 넣어준다.
**const handleOnClickPlusNum = (payload) => {
dispatch(_addNumber(+payload));
};**
Thunk에서 Promise 객체를 다루는 법 + redux를 이용해서 state 관리
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
const initialState = {
todos: [],
isLoading: false,
error: null,
};
// // 액션 객체를 dispatch 해주는 api fulfillvalue, rejectwithvalue를 포함하는 Thunk 함수
export const __getTodos = createAsyncThunk(
"todos/getTodos",
async (payload, thunkAPI) => {
try {
const data = await axios.get("<http://localhost:3001/todos>");
console.log(data);
return thunkAPI.fulfillWithValue(data.data);
} catch (error) {
console.log(error);
return thunkAPI.rejectWithValue(error);
}
}
);
const todoSlice = createSlice({
name: "todo",
initialState,
reducers: {},
extraReducers: {
[__getTodos.pending]: (state) => {
state.isLoading = true;
},
[__getTodos.fulfilled]: (state, action) => {
state.isLoading = false;
state.todos = action.payload;
},
[__getTodos.rejected]: (state, action) => {
state.isLoading = false;
state.error = action.payload;
},
},
});
export const {} = todoSlice.actions;
export default todoSlice.reducer;
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { __getTodos } from "../modules/todoSlice";
const Todos = () => {
const dispatch = useDispatch();
const { isLoading, error, todos } = useSelector((state) => state.todo);
console.log(isLoading, error, todos);
useEffect(() => {
dispatch(__getTodos());
}, [dispatch]);
if (isLoading) {
return <div>로딩 중</div>;
}
if (error) {
return <div>{error.message}</div>;
}
return todos.map((item) => {
return (
<div key={item.id}>
<div>{item.title}</div>
<div>{item.content}</div>
</div>
);
});
};
export default Todos;
memo(hook은 아니고 알아야할 개념), useMemo, useCallback
memo:
컴포넌트에 불필요한 렌더링을 하지 않도록 하는 함수
불필요한 렌더링 == 화면에서 변경이 없는데도 리렌더링되는 경우
리렌더링 조건 (복습)
- 부모 컴포넌트 렌더링
- 컴포넌트의 state 변경
- 부모로부터 전달받는 props 값이 변경된 경우
불필요한 렌더링을 줄이면 프로젝트의 부하를 줄이고 퍼포먼스 능력을 향상 시킬 수 있다.
부모 컴포넌트가 리렌더링이 되더라도 자식 컴포넌트가 리렌더링 되지 않게 하는 처리
import {memo} from “react”
const Button = () => {
return <div>
<button>버튼</button>
</div>}
export default memo(Button);
useCallback vs useMemo
useCallback : 프롭스로 넘겨주는 함수는 선언된 장소인 컴포넌트가 리렌더링 될 때 재 생성 되는데, useCallback을 써주면 재 생성되지 않는다.
import {useCallback} from "react";
const onClickHandler = useCallback(() ⇒ {
console.log("hellobutton")
},[]);
useMemo : 프롭스로 넘겨주는 객체가 위치한 장소의 컴포넌트가 리렌더링 될 때 객체도 재생성 되는데, useMemo를 써주면 재 생성되지 않는다.
useCallback과 같은 데 배열이나, 객체의 재 생성을 막아 줄 때 쓴다.
import {useMemo} from "react";
const data = useMemo(()=>{
return [
{
id:1,
title:"react",
},
]},[]);
원시 데이터: let a= 1 이런 식의 데이터들은 useMemo를 안 써도 리렌더링 하지 않는다.
위의 최적화 hook을 남용하면 안되는 이유
- 렌더링 전 후의 값을 비교해서 같지 않으면 재생성하고 같으면 재생성을 하지 않는데 ? (강사님이 내가 생각한 것과 반대로 얘기하시는데 뭘 비교하는 거지) 이러한 비교 과정이 불필요하게 들어갈 수 있다.
Custom Hook
반복되는 로직이나 기능을 우리가 Custom Hook으로 만들어서 관리 할 수 있다.
useInput이라는 커스텀 훅을 만들어보자 - 이름은 마음대로 지어되나, 앞에 use를 붙여야한다.
'개발자 되는 중 > 개발 공부' 카테고리의 다른 글
후발대 - axios, 비동기, promise (0) | 2023.01.02 |
---|---|
TIL 제대로 쓰는 법 (0) | 2022.12.22 |
React uuid 쓰는 법 (0) | 2022.12.20 |
애자일 방법론 특강 (0) | 2022.12.16 |
CS 특강 - CPU (0) | 2022.12.15 |