Today I Learned 2023.03.12. [React 심화3]

Dongchan Alex Kim·2023년 3월 11일
0

Today I Learned

목록 보기
2/31
post-thumbnail

Axios - patch

patch는 보통 어떤 데이터를 수정하고자 서버에 요청을 보낼 때 사용하는 메서드이다.

 const [targetId, setTargetId] = useState(null);
 const [editTodo, setEditTodo] = useState({
    title: "",
  });

 const onClickEditButtonHandler = () => {
    axios.patch(`http://localhost:3001/todos/${targetID}`, edit);
    setTodos(todos.map((item)=>{
    if(item.id == targetID){
      return{...item, title : content}
    }else{
      return item
    }
  };


{/* 수정기능에 필요한 id, 수정값 input2개와 수정하기 버튼을 추가 */}
<div>
  <input
    type="text"
    placeholder="수정하고싶은 Todo ID"
    onChange={(ev) => {
      setTargetId(ev.target.value);
    }}
    />
  <input
    type="text"
    placeholder="수정값 입력"
    onChange={(ev) => {
      setEditTodo({
        ...editTodo,
        title: ev.target.value,
      });
    }}
    />
  <button
    // type='button' 을 추가해야 form의 영향에서 벗어남
    type="button"
    onClick={onClickEditButtonHandler}
    >
    수정하기
  </button>
</div>

저번에도 말했지만, set함수는 마치 서버에서 받아온 것처럼 렌더링하기 위해서 추가하는 옵션이다.
새로고침을 해야만 새로운 정보가 표시되는 이유가 뭘까
ID값은 자동으로 추가되지만, 이를 표시하기 위한 수단은 없다.(state가 없다)
그렇기 때문에, 이를 렌더링하기 위해서는 fetch함수를 써서 그냥 서버에 있는 것들을 가져와야 렌더링이 될 것이다.

instance와 interceptor

axios interceptor는 이름에서 알 수 있듯, 다음 두 상황에서 흐름을 가로채서 여러분이 어떠한 코드 상의 관여를 할 수 있게 한다.

  • 요청 헤더 추가
  • 인증 관리
  • 로그 관련 로직 삽입
  • 에러 핸들링

이런 곳에서 쓰일 수 있다고 하니 무조건 머리에 박아두자.

//app.js
//조회함수
const fetchTodos = async() => {
  const {data} = await api.get();
  setTodos(data);
}
//src>axios폴더>api.js
import axios from "axios";

const instance = axios.create({
  baseURL: "http://localhost:4000",
});

instance.interceptors.request.use(
  function (config) {
    // 요청을 보내기 전 수행
    console.log("인터셉트 요청 성공!");
    return config;
  },
  function (error) {
    // 오류 요청을 보내기 전 수행
    console.log("인터셉트 요청 오류!");
    return Promise.reject(error);
  }
);

instance.interceptors.response.use(
  function (response) {
    console.log("인터넵트 응답 받았어요!");
    // 정상 응답
    return response;
  },

  function (error) {
    console.log("인터셉트 응답 못받았어요...ㅠㅠ");
    return Promise.reject(error);
  }
);

export default instance;

요청과 응답 중간에 가로채서 어떠한 작업을 수행시킬 수 있다. token 등 인증 관련 로직 적용할 수 있겠다!

Thunk

미들웨어
리덕스에서 dispatch를 하면 action 이 리듀서로 전달이 되고, 리듀서는 새로운 state를 반환하는데, 미들웨어를 사용하면 이 과정 사이에 하고 싶은 작업들을 넣을 수 있습니다. 많이 사용되고 있는 리덕스 미들웨어는 Redux-thunk이다.

//thunk 함수는 createAsyncThunk 라는 툴킷 API를 사용해서 생성합니다.
//__가 함수 이름에 붙는 이유는 이 함수가 thunk 함수라는 것을 표시하기 위한 것이다.
//counterSlice.js
export const __addNumber = createAsyncThunk(
	"ADD_NUMBER_WAIT",
	(payload, thunkAPI) => {
   setTimeout(() => {
     thunkAPI.dispatch(addNumber(payload));
   }, 3000);
 });



//app.js
const onClickAddNumberHandler = () => {
  dispatch(__addNumber(number));
};

thunk 함수를 디스패치한다. payload는 thunk함수에 넣어주면 리덕스 모듈에서 payload로 받을 수 있다.

thunk로 서버로부터 데이터를 가져와서 store에 dispatch 하기
: 리듀서 로직 구현

  • extraReducers 사용: reducers에서 바로구현되지 않는 기타 Reducer로직을 구현할 때 사용하는 기능이다. 보통 thunk 함수를 사용할 때 extraReducers를 사용합니다.
  • 통신 진행중, 실패, 성공에 대한 케이스를 모두 상태로 관리하는 로직을 구현합니다. 서버와의 통신은 100% 성공하는 것이 아니기 때문에 서버와 통신을 실패했을때도 우리의 서비스가 어떻게 동작할지 우리는 구현해야 한다. 서버와 통신을 진행하고 있는 ‘진행중' 상태일때 어떻게 작동해야할지 마찬가지로 구현해야 한다.
//todoSlice.js
const initialState = {
  todos: [],
  isLoading: false,
  error: null,
};

export const __getTodos = createAsyncThunk(
  "todos/getTodos",
  async (payload, thunkAPI) => {
    try {
      const data = await axios.get("http://localhost:3001/todos");
      return thunkAPI.fulfillWithValue(data.data);
    } catch (error) {
      return thunkAPI.rejectWithValue(error);
    }
  }
);

export const todosSlice = createSlice({
  name: "todos",
  initialState,
  reducers: {},
  extraReducers: {
    [__getTodos.pending]: (state) => {
      state.isLoading = true; // 네트워크 요청이 시작되면 로딩상태를 true로 변경합니다.
    },
    [__getTodos.fulfilled]: (state, action) => {
      state.isLoading = false; // 네트워크 요청이 끝났으니, false로 변경합니다.
      state.todos = action.payload; // Store에 있는 todos에 서버에서 가져온 todos를 넣습니다.
    },
    [__getTodos.rejected]: (state, action) => {
      state.isLoading = false; // 에러가 발생했지만, 네트워크 요청이 끝났으니, false로 변경합니다.
      state.error = action.payload; // catch 된 error 객체를 state.error에 넣습니다.
    },
  },
});
//app.js
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { __getTodos } from "./redux/modules/todosSlice";

const App = () => {
  const dispatch = useDispatch();
  const { isLoading, error, todos } = useSelector((state) => state.todos);

  useEffect(() => {
    dispatch(__getTodos());
  }, [dispatch]);

  if (isLoading) {
    return <div>로딩 중....</div>;
  }

  if (error) {
    return <div>{error.message}</div>;
  }

  return (
    <div>
      {todos.map((todo) => (
        <div key={todo.id}>{todo.title}</div>
      ))}
    </div>
  );
};

export default App;

useSelector를 이용해서 initialState에서의 isLoading, error, todos를 구조분해로 받아온 후,
아까 todoSlice.js에서
[__getTodos.pending] /
[__getTodos.rejected] /
[__getTodos.fulfilled] /
로 extrareducers : { } 안에서 처리되어서 if (isLoading) 안에서 true false값이 판단 되어지기 때문에, 이에 맞는 화면 구성을 app.js에서 컨트롤 할 수 있겠다.

profile
Disciplined, Be systemic

0개의 댓글