Github-finder (2)
#Input Reducerの作成#
1)/modules/input.jsの作成
: input reducerを作成し、usernameというステータスを作成します.
import { createAction, handleActions } from 'redux-actions';
const CHANGE_INPUT = 'input/CHANGE_INPUT';
export const changeInput = createAction(CHANGE_INPUT, (username)=>({
username,
}));
const initState = {
username: "",
};
const input = handleActions(
{
[CHANGE_INPUT]: (state, { payload: {username}}) => ({
...state,
username,
}),
},
initState
)
export default input;
2)rootReducerに追加import { combineReducers } from "redux";
import { all } from "redux-saga/effects";
import input from './input';
const rootReducer = combineReducers({
input,
});
export default rootReducer;
3) InputContainer.jsでの使用: Input reducerに含まれるusernameというstateを使用します.
useDispatch()とuseSelector()の使用
import React, {useRef} from 'react';
import InputComponent from '../components/InputComponent';
import {useDispatch, useSelector} from "react-redux";
import {changeInput} from '../modules/input';
function InputContainer() {
const dispatch = useDispatch();
/* 입력받는 값을 실시간으로 username에 업데이트 */
const onHandleInputChange = (e)=>{
const value = e.target.value;
dispatch(changeInput(value));
};
return (
<>
<InputComponent
onHandleInputChange={onHandleInputChange}>
</InputComponent>
</>
)
}
export default InputContainer
#user Reducer作成と使用#
1)ユーザー情報
: https://api.github.com/users/[계정이름]の応答情報を保存
2)reposInfo(ユーザのrepo情報)
: https://api.github.com/users/[アカウント名]/reposの応答情報
1)/modules/user.jsの作成
import { createAction, handleActions } from 'redux-actions';
import createRequestSaga from "../lib/createRequestSaga";
import { createRequestActionTypes } from "../lib/createRequestSaga";
import * as userAPI from "../lib/api/user";
import { takeLatest } from "redux-saga/effects";
/* GET_USER에 대한 성공,실패 액션을 생성 */
const [GET_USER, GET_USER_SUCCESS, GET_USER_FAILURE] = createRequestActionTypes(
"user/GET_USER"
);
const [GET_REPOS, GET_REPOS_SUCCESS, GET_REPOS_FAILURE] = createRequestActionTypes(
"user/GET_REPOS"
);
/* 액션 호출 함수 생성 */
export const getUser = createAction(GET_USER, (username) => username);
export const getRepo = createAction(GET_REPOS, (username) => username);
/* 해당하는 액션 호출시 Saga실행 */
const getUserSaga = createRequestSaga(GET_USER, userAPI.userInfo);
const getRepoSaga = createRequestSaga(GET_REPOS, userAPI.userRepo);
/* 요청된 것들 중 가장 마지막 요청만 처리 (여러번 클릭시 모두 처리되면 매우 비효율적!) */
export function* userSaga(){
yield takeLatest(GET_USER, getUserSaga);
yield takeLatest(GET_REPOS, getRepoSaga);
}
/* State 초기값 */
const initState = {
userInfo: null,
error: null,
reposInfo: null,
}
/* 액션을 store에 저장하는 리듀서를 handleActions로 쉽게 처리! */
const user = handleActions(
{
[GET_USER_SUCCESS]: (state, { payload: userInfo }) => ({
...state,
userInfo,
}),
[GET_USER_FAILURE]: (state, { payload: error }) => ({
...state,
error,
}),
[GET_REPOS_SUCCESS]: (state, { payload: reposInfo }) => ({
...state,
reposInfo,
}),
[GET_REPOS_FAILURE]: (state, { payload: error }) => ({
...state,
error,
}),
},
initState
);
export default user;
2)rootReducerに追加import { combineReducers } from "redux";
import { all } from "redux-saga/effects";
import input from './input';
import user, {userSaga} from './user';
const rootReducer = combineReducers({
input,
user
});
/* Saga사용을 위해 생성한 userSaga()를 추가! */
export function* rootSaga() {
yield all([userSaga()]);
}
export default rootReducer;
3) InputContainer.変更: フォームの作成後にフォームをコミットするときにgetUser()/getRepo()を追加
function InputContainer() {
const dispatch = useDispatch();
//const nameInput = useRef();
/* dispatch로 값 넘기기 위해 useSelector로 값 가져옴 */
const {username} = useSelector(({input})=>({
username: input.username,
}));
const onHandleInputChange = (e)=>{
const value = e.target.value;
dispatch(changeInput(value));
};
/* 추가된 부분의 핵심 */
const onHandleFormSubmit = (e) => {
e.preventDefault();
dispatch(getUser(username));
dispatch(getRepo(username));
}
return (
<>
<InputComponent
onHandleInputChange={onHandleInputChange}
onHandleFormSubmit={onHandleFormSubmit}
username={username}
</InputComponent>
</>
)
}
4) ResultContainer.変更: 保存したuserInfo/reposInfostateをコンポーネントに転送
import React from 'react'
import ResultComponent from '../components/ResultComponent';
import { useSelector } from "react-redux";
function ResultContainer() {
/* 필요한 state들을 useSelector로 store에서 가져온다 */
const {userInfo, reposInfo, loading} = useSelector(({user, loading})=>({
userInfo: user.userInfo,
reposInfo: user.reposInfo,
loading: loading["user/GET_REPOS"],
}));
return (
<>
<ResultComponent loading={loading} userInfo={userInfo} reposInfo={reposInfo}>
</ResultComponent>
</>
)
}
export default ResultContainer
5) ResultComponent.変更: 必要に応じてContainerから送信されたuserInfoとreposInfo(条件レンダリング)を出力します.
function SearchResult({userInfo, reposInfo, loading}) {
return (
<>
{
userInfo &&
<CardTemplate>
<Avatar src={userInfo.avatar_url}/>
<UserInfo>
<h2>{userInfo.name}</h2>
<p>{userInfo.bio}</p>
<FollowTemplate>
<Follow><strong>Followers</strong> {userInfo.followers}</Follow>
<Follow><strong>Following</strong> {userInfo.following}</Follow>
<Follow><strong>Repos</strong> {userInfo.public_repos}</Follow>
</FollowTemplate>
<div>
{
reposInfo ?
reposInfo.slice(0,10).map((repo, idx)=>(
<Repo key={idx} href={repo.html_url} target="_blank">{repo.name}</Repo>
))
: (loading == true? <Loading>LOADING</Loading>:null)
}
</div>
</UserInfo>
</CardTemplate>
}
</>
);
}
export default SearchResult
# loading Reducer #
: startLoading()/finishLoading()と適切なreducer関数の作成
import { createAction, handleActions } from "redux-actions";
const START_LOADING = "loading/START_LOADING";
const FINISH_LOADING = "loading/FINISH_LOADING";
export const startLoading = createAction(
START_LOADING,
(requestType) => requestType
);
export const finishLoading = createAction(
FINISH_LOADING,
(requestType) => requestType
);
const initState = {};
const loading = handleActions(
{
[START_LOADING]: (state, action) => ({
...state,
[action.payload]: true,
}),
[FINISH_LOADING]: (state, action) => ({
...state,
[action.payload]: false,
}),
},
initState
);
export default loading;
2)/lib/createRequestSaga.jsの作成: 以前にgetUserSaga/getRepoSagaを作成したときなどのリクエスト
createRequestSagaを使用するため、リクエストの前後
startLoading()/finishLoading()!
import { put, call } from "redux-saga/effects";
import { startLoading, finishLoading } from "../modules/loading";
export const createRequestActionTypes = (type) => {
const SUCCESS = `${type}_SUCCESS`;
const FAILURE = `${type}_FAILURE`;
return [type, SUCCESS, FAILURE];
};
export default function createRequestSage(type, request) {
const SUCCESS = `${type}_SUCCESS`;
const FAILURE = `${type}_FAILURE`;
return function* (action) {
/* 요청 전에 값을 true로 변환 */
yield put(startLoading(type));
try {
const response = yield call(request, action.payload);
yield put({
type: SUCCESS,
payload: response.data,
meta: response,
});
} catch (error) {
yield put({
type: FAILURE,
payload: error,
error: true,
});
}
/* 요청이 끝나고 값을 false로 변환 */
yield put(finishLoading(type));
};
}
3)構成部品からのロード値の取得: storeから値をインポートすると、trueまたはfalseの値を持つ特定のアクションにloadがインポートされます.
: ロード状態に応じた条件付きレンダリング
Reference
この問題について(Github-finder (2)), 我々は、より多くの情報をここで見つけました https://velog.io/@neity16/React-Github-finder-2テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol