React 총정리 ondo

김용희·2022년 2월 9일
0
Updated upstream
scss 에서 주로 부모 태그 id를 #root로 지정한다.
단순 리액트에서는 html의 부모 id가 #root 이지만
next.js 에서는 부모 id가 #__next 이다 
추가설정없이는 전역 css와 
_app에 다른페이지의 css를 간편하게 모으는것도 방법이지만
이경우 각자의 페이지마다 className을 구체적으로 떨어뜨려 중복을 방지하는것이 좋다.
HYDRATE 는 conbineReducer에서 리듀서를 합칠때 
서버사이드 렌더링을 위해서 action.tpye의 case부분에 작성한다 
컴바인리듀서에서 초기상태값도 자동으로 다른객체로 합쳐서부른다 
useSelector에서 중앙상태값을 받아쓰고
useCallback안의 dispatch(액션함수({인자1,인자2})) 로 해당 액션 사용 
구조분해할당할때 
user에서 age를 받는다고 할때
const age = useSelector((state)=> state.user.age); 로 부르는데
const {age} = useSelector((state)=>state.user);  로도 부를수있다
객체 깊이가 한단계 낮아지니 유의
옵셔널 체이닝(optional chaining)
const id = useSelector((state)=> state.user.login && state.user.login.id);
const id = useSelector((state)=> state.user.login?.id);
있나 없나 의심 되는부분에는 ? 로 대체가능 
export default function* rootSaga() {
  yield all([
    fork(feedSaga),
    fork(userSaga),
  ]);
  all은 배열은 받는다 
  
  fork에서 feedSaga는
  function* feedSaga() {
    yield take('액션타입')
  }
  
을 사용한다 

redux

const reducer = (state = initialState, action ) => {
 switch (action.type) {
   casse 'LOG_IN_REQUSET';
     return {
        ...state,
        isLoggingIn: true,
        }
   case  'LOG_IN_SUCCESS';
      return{
      
      

redux toolkit

export const feedReducer = feedSlice.reducer;
export const feedAction = feedSlice.actions; //리듀서 안에 정의된 함수 (state,action)
----
========================================================================================
import { createSlice } from '@reduxjs/toolkit'
const todosSlice = createSlice({
 name: 'todos',
 initialState: [],
 reducers: {
   addTodo(state, action) {
     const { id, text } = action.payload
     state.push({ id, text, completed: false })
   },
   toggleTodo(state, action) {
     const todo = state.find(todo => todo.id === action.payload)
     if (todo) {
       todo.completed = !todo.completed
     }
   }
 }
})
export const { addTodo, toggleTodo } = todosSlice.actions
export default todosSlice.reducer
========================================================================================
createSlice는 다음 옵션과 함께 option 객체를 인자로 사용합니다.
takes an options object as its argument, with these options:
- name: 생성 된 action types를 생성하기 위해 사용되는 prefix
- initialState: reducer의 초기 상태
- reducers: key는 action type문자열이 되고 함수는 해당 액션이 dispatch될때 실행될 reducer입니다.
(switch-case문과 비슷해서 "case reducers"라고도 합니다.)
따라서, "todos/addTodo"액션이 dispatch될 때 addTodoreducer가 수행됩니다.
default핸들러는 없습니다. createSlice에 의해 생성된 리듀서는 
현재 dispatch된 액션이 아닌 다른 액션들에 대해 자동으로 현재 상태를 반환하도록 처리되어 있기 때문에, 
직접 핸들링해주지 않아도 됩니다

redux-saga/effects

redux-saga/effects 에서 
yield의 종류 (항상 앞에 붙는다)   yield call은 await과 비슷하다 
yeild 쓰는 이유는 단순 동작을 넘어서 동작이 잘 되는지 보장 받을 필요가 있다.
test할때 끊어서 실행되니 문제점을 파악하기 쉽다 
ex) const l = logIn({type: "LOG_IN_REQUEST', data: {id : 'zerocho@gamil.com'}})
    l.next();
   next할때마다 yield 부분까지만 실행 

all : 배열안에 값을 모두 실행

fork : function* 제너레이터 비동기 함수 실행  (결과를 기다리지않고 다음 줄 실행) (논블록킹)
     axios.post('/api/login')
     yield put({
       type: 'LOG_IN_SUCCESS',
       data: result.data
     })
     
★ call : function* 제너레이터 동기 함수 실행  (결과를 기다리고 다음 줄 실행) 
       axios.post('/api/login')
       .then(() =>{
           yield put({
           type: 'LOG_IN_SUCCESS',
           data: result.data
     })
       })
     
take : 동기적으로 동작  1회용, 딱 한번만 사용되고 사용불가  
   while(ture) {
    yield take('LOG_IN_REQUEST', login)
    }
    와일로 테이크를 감싸주면 여러번사용가능
takeEvery : 비동기적으로 동작  와일 테이크와 기능 같음 
★ takeLatest : 마지막으로 호출된것만 실행 (실수로 두번 호출해서 둘 다 로딩중일때 마지막으로 로딩된 것만 호출, 이미 완료된 호출은 해당사항 없음)
                                         요청 2번일경우 마지막 요청에대해서만 응답,   요청은 2번 무조건 다 감, 응답을 취소
takeLeading : 가장 먼저 호출된것만 실행 
throttle : 일정시간후 실행  yield throttle('ADD_POST_REQUST', addPost, 2000);  스크롤을 내리거나 올릴때 사용
debouncing : 연이어 호출되는 함수들중 마지막함수 (또는 제일 처음)만 호출하게 한다. ajax에서 주로사용 
                         ,검색창에 한글자한글자 할때마다 호출하기보다 어떤 단어가 완성됬을때만 요청보낼떄 
put : dispatch (액션함수 실행)

전체적인 redux-saga 흐름 정의

function* watchLogin() {
yield take('LOG_IN_REQUEST', logIn);
'LOG_IN_REQUEST'이라는 액션이 실행(take)될때까지 기다리다가(yield)
logIn이라는 제너레이터함수(function* login)을 실행한다.

즉 'LOG_IN_REQUEST'이라는 액션이 들어오면 logIn 제너레이터 함수(function*) 실행 


function* logIn() {
 yield call(logInAPI)     /
 }
 
function loginAPI( 전달할 로그인 데이터) {   //여기서는 제너레이터함수(*)가 아니다
 return axios.post('/api/login', 전달할 로그인 데이터 )
}

전체적인 redux-saga 흐름

//이벤트 리스너의 역할 

 function* watchLogin() {
   yield take('LOG_IN_REQUEST', logIn);

 function* logIn(action) {
   const result = yield call(logInAPI, action.data, 'a', 'b')    //  logInAPI(action.data) == yeild call(logInAPI, action.data)  첫번째 자리가 함수 그 다음이 매개변수
   yield put({
     type: 'LOG_IN_SUCCESS',
     data: result.data
   })
 }
 
  function loginAPI( data a, b) {   //여기서는 제너레이터함수(*)가 아니다
 return axios.post('/api/login', data )    
}

saga 상세 흐름

function* watchLogin() {
    yield take('LOG_IN_REQUEST', logIn);
                      ex)     이벤트 리스너 역할  
                          <!--         <script> 
                                  const a = document.querySelector('a');
                                  a.addEventListener('click', showConsole);
                                  function showConsole() { console.log("콘솔로그 실행"); } 
                                  </script>
                           -->
 
 function* logIn() {
  const result = yield call(logInAPI)
  }
  서버로 로그인하는 요청의 결과를 받는다 
  
  function* logIn() {
  const result = yield call(logInAPI)
  yield put({
    type: 'LOG_IN_SUCCESS',
    data: result.data
  })
 }
 받은결과 함수실행(put)
 put은 dispatch 
  
  function* logIn() {
  try{
  const result = yield call(logInAPI)
  yield put({
    type: 'LOG_IN_SUCCESS',
    data: result.data
  })
 } catch (err) {
  yield put({
   type : 'LOG_IN_FAILURE',
   data : err.response.data,
 })
 }
}
성공결과는 result.data,
실패 결과는 err.response.data
비동기요청함수의 액션  
REQUEST
SUCCESS
FAILURE 나눈다 
axios.get('http://placeholder.com')
.then((result)=>{
result.data
}).catch((err)=>
err)
axios의 result에는 {data (요청한값) , status:200,404 ,statusText:"", headers:{...}, config:{...}}
등등의 데이터가 오는데 원하는 요청값은 data로 지정되어있다.  

dispatch쓰는이유

단순히 해당 page에서 axios.get으로 데이터를 불러들여오면 편한데 왜 dispatch와 redux-saga를 쓰는 이유는
saga가 redux의 middleware로서 redux로 중앙상태값을 변경시킬때 동시에 saga도 실행되어서
saga 이벤트리스너 역할을 하는 function* (리덕스타입 or 리덕스툴킷일때는 리덕스의 함수값 , 사가 비동기통신 함수)
를 호출할수있어서 동시에 전역의 중앙상태값을 axios통신과함께 바꿀수 있기 때문에 dispatch를 쓴다.
해당 saga는 처음 request에서 api함수를 호출하고 그 호출값에따라 success일 경우 
성공한 데이터값을 리덕스타입 or 리덕스툴일때는 리덕스의 함수값을 불러 해당 함수의 action.payload 로 인자값을 넘겨준다.
넘겨진 인자값은 해당 리덕스 함수의 파라미터로 가서 리덕스 로직에 따라 중앙상태값을 변경시킨다.
해당 변경된 중앙상태값이 store에 저장되고 그 저장된 store를 _app.tsx 전역 페이지에 넘김으로써 모든 컴포넌트에게 공유시킨다. 
dispatch는 보통 
페이지가 처음 렌더링될때 useEffect로 부르거나 
useCallback 으로 어떤 상태값이 변경되었을때 사용한다.
useSelector는 리덕스에서 상태값을 가져올때 사용한다. 

reducer 흐름도

이니셜스테이트 -> 리듀서로 상태변환 -> 디스패치로 리듀서 사용 ,  리듀서를 쓸때 사가 사용 -> 사가에서 API 호출 -> 호출값을 리덕스에 삽입 - 중앙 상태 변화
사가로 미들웨어가 걸려져 있어도 사가 안쓰고 단순히 리덕스만 사용가능!

axios 비동기값 확인

const __GetFeedState = (token: string | null) => {
return axios({
method: "GET",
url: "http://localhost:8080/feed",
// url: "https://jsonplaceholder.typicode.com/comments",
headers: { Authorization: "Bearer " + token },
})
.then((res) => {
console.log(res);
return res.data;
})
.catch((err) => {
return err;
});
};
useEffect(() => {
const token = localStorage.getItem("Token");
GetFeedState(token);
}, [GetFeedState]);

profile
He threw his knapsack over the brick wall

0개의 댓글