Main Course 주특기 3강 - React

박경준·2021년 6월 26일
0

main course - react

목록 보기
3/6

주특기 3강!

  1. React-router로 주소에 따라 다른 페이지를 보여줄 수 있다.
  2. 미리 정해놓은 주소가 아닐 때, '앗! 잘못 찾아오셨어요!' 페이지를 보여줄 수 있다.
  3. Redux로 상태관리를 해보고 아래의 상태관리 흐름을 이해한다.
    (기본 값이 있고 → 어떤 동작을 하면("어떤 동작을 하는 지 정하자! → 하면 뭐가 바뀌는 지 정하자!" 과정이 사전에 필요하다!) → 값이 변했잖아? → 컴포넌트한테 알려주자!)
  4. Redux hook을 사용해본다.

Route

yarn add react-router-dom

index.js에 BrowserRouter 적용

...
import { BrowserRouter } from "react-router-dom";

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

...
  • Route 사용방법 1: 넘겨줄 props가 없을 때
<Route path="주소[/home 처럼 /와 주소를 적어요]" component={[보여줄 컴포넌트]}/>
  • Route 사용방법 2: 넘겨줄 props가 있을 때
<Route path="주소[/home 처럼 /와 주소를 적어요]" render={(props) => (<BucketList list={this.state.list} />)} />

App.js에 Route 적용

...
import { Route } from "react-router-dom";

...  
  render(){
    return (
      <div className="App">
        <Route exact path="/" component={Home} />
        {/* '/' 이 기호가 /cat에도 있고 home path도 / 라서 메인으로 들어가면 컴포넌트가 중복 호출됨 -> exact 추가 */}
        <Route path="/cat" component={Cat} />
        <Route path="/dog" component={Dog} />
      </div>
    );
  }
...

URL 파라미터 사용하기

//App.js
...
// 파라미터 주기
<Route path="/cat/:cat_name" component={Cat}/>

...
//Cat.js
...
const Cat = (props) => {
  console.log(props.match.params.cat_name);
  return (
    <div>고양이 화면이에요.</div>
  )
}
...

링크 이동 시키기

  1. Link 컴포넌트 이용하기
// App.js
...
import { Link } from "react-router-dom";
...

<div>
  <Link to="/">Home으로 가기</Link>
  <Link to="/cat">Cat으로 가기</Link>
  <Link to="/dog">Dog으로 가기</Link>
</div>
  1. history 객체를 이용한 함수 활용하기
    history 객체를 이용하기 위해서는 {withRouter}를 불러와야함
// App.js
...
import { withRouter } from "react-router";
...
<button onClick={() => {
  // props에 있는 history를 사용합니다.
  // push([이동할 주소])는 페이지를 이동시켜 줍니다.
  this.props.history.push('/cat');
}}>
/cat으로 가기
</button>
<button onClick={()=>{
  // goBack()은 뒤로가기 예요.
  this.props.history.goBack();
}}>뒤로가기
</button>
...
export default withRouter(App); // 내보내는 부분에서 withRouter로 감싸줍니다

잘못된 주소 처리하기

// App.js
...
import { Route, Switch } from "react-router-dom";
  ...
  render() {
    return (
      <div className="App">
        ...
          <Switch> // Switch가 모든 Route를 감싸야 정상 동작함.
            <Route
              path="/"
              exact
              render={(props) => (
                <BucketList
                  list={this.state.list}
                  history={this.props.history}
                />
              )}
            />
            <Route path="/detail" component={Detail} />
            <Route component={NotFound} /> // path를 안적음으로써 없는 path에 갔을때 NotFound 컴포넌트가 불러와짐.
          </Switch>
        ...
      </div>
    );
  }
...

Redux

yarn add redux react-redux
  • 리덕스 구성 요소
    State, Action, ActionCreator, Reducer, Store, dispatch

  • Redux의 3가지 특징

  1. store는 하나만 쓴다.

  2. store의 state(데이터)는 오직 action으로만 변경할 수 있다!

  3. 어떤 요청이 와도 리듀서는 같은 동작을 해야한다!

    리듀서는 순수한 함수여야 한다는 말입니다.
    순수한 함수라는 건,

  • 파라미터 외의 값에 의존하지 않아야하고,
  • 이전 상태는 수정하지(=건드리지) 않는다. (변화를 준 새로운 객체를 return 해야합니다.)
  • 파라미터가 같으면, 항상 같은 값을 반환
  • 리듀서는 이전 상태와 액션을 파라미터로 받는다.

Redux 모듈 만들기

// /src/redux/modules/bucket.js

// Actions
const LOAD = "bucket/LOAD";
const CREATE = "bucket/CREATE";
const DELETE = "bucket/DELETE";

const initialState = {
  list: ["영화관 가기", "매일 책읽기", "수영 배우기"],
};

// Action Creators
export const loadBucket = (bucket) => {
  return { type: LOAD, bucket };
};

export const createBucket = (bucket) => {
  return { type: CREATE, bucket };
};

export const deleteBucket = (bucket) => {
  return { type: DELETE, bucket };
};

// Reducer
export default function reducer(state = initialState, action) {
  switch (action.type) {
    // do reducer stuff
    case "bucket/LOAD":
      return state;

    case "bucket/CREATE":
      const new_bucket_list = [...state.list, action.bucket];
      return {list: new_bucket_list};

    case "bucket/DELETE":
      const bucket_list = state.list.filter((l, idx) => {
        return idx !== action.bucket
      });
      return {list: bucket_list};

    default:
      return state;
  }
}
// /redux/configStore.js
import { createStore, combineReducers } from "redux";
import bucket from './modules/bucket';
import { createBrowserHistory } from "history";

// 브라우저 히스토리를 만들어줍니다.
export const history = createBrowserHistory();
// root 리듀서를 만들어줍니다.
// 나중에 리듀서를 여러개 만들게 되면 여기에 하나씩 추가해주는 거예요!
const rootReducer = combineReducers({ bucket });

// 스토어를 만듭니다.
const store = createStore(rootReducer);

export default store;

앱에 store 연결하기

// index.js
...
// 우리의 버킷리스트에 리덕스를 주입해줄 프로바이더를 불러옵니다!
import { Provider } from "react-redux";
// 연결할 스토어도 가지고 와요.
import store from "./redux/configStore";
ReactDOM.render(
  <Provider store={store}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>,
  document.getElementById("root")
);

클래스형 컴포넌트에서 Redux 사용하기

1) 리덕스 모듈과 connect 함수를 불러옵니다.
2) 상태값을 가져오는 함수와 액션 생성 함수를 부르는 함수를 만들어줍니다.
3) connect로 컴포넌트와 스토어를 엮어줍니다.
4) 콘솔에 this.props를 찍어봅니다. (스토어에 있는 값이 잘 나왔는지 볼까요!)
5) this.state에 있는 list를 지우고 스토어에 있는 값으로 바꿔봅시다!
6) setState를 this.props.create로 바꿔봅시다!

// App.js
...
// 리덕스 스토어와 연결하기 위해 connect라는 친구를 호출할게요!
import {connect} from 'react-redux';
// 리덕스 모듈에서 (bucket 모듈에서) 액션 생성 함수 두개를 가져올게요!
import {loadBucket, createBucket} from './redux/modules/bucket';

// 이 함수는 스토어가 가진 상태값을 props로 받아오기 위한 함수예요.
const mapStateToProps = (state) => ({
  bucket_list: state.bucket.list,
});

// 이 함수는 값을 변화시키기 위한 액션 생성 함수를 props로 받아오기 위한 함수예요.
const mapDispatchToProps = (dispatch) => ({
  load: () => {
    dispatch(loadBucket());
  },
  create: (new_item) => {
    dispatch(createBucket(new_item));
  }
});

...

  addBucketList = () => {
    const new_item = this.text.current.value;
    this.props.create(new_item); // Redux의 create type을 적용해준 모습!
  };

  render() {
    return (
      ...
          <Switch>
            <Route
              path="/"
              exact
              render={(props) => (
                <BucketList
                  list={props.bucket_list} // store의 bucket_list를 props로 보내줌. (자식 컴포넌트에서도 store로 불러주면 되긴하지만, 아직 해당 작업을 안해줌.)
                  history={this.props.history}
                />
              )}
            />
			...
          </Switch>
        ...
      </div>
    );
  }
}
...
// withRouter 적용
// connect로 묶어줬습니다!
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(App));

함수형 컴포넌트에서 Redux 사용하기

Redux도 함수형 컴포넌트에서 쓰는 Hook이 있음
useSelector(), useDispatch()

// BucketList.js
...
// redux hook을 불러옵니다.
import {useDispatch, useSelector} from 'react-redux';

const BucketList = (props) => {
  // 버킷리스트를 리덕스 훅(useSelector)으로 가져오기
  const bucket_list = useSelector(state => state.bucket.list);
  
  return (
    <ListStyle>
      {bucket_list.map((list, index) => {
        return (
          <ItemStyle
            className="list_item"
            key={index}
            onClick={() => {
              props.history.push("/detail");
            }}
          >
            {list}
          </ItemStyle>
        );
      })}
    </ListStyle>
  );
};
...
// Detail.js
...
// redux hook을 불러옵니다.
import { useDispatch, useSelector } from "react-redux";
// 내가 만든 액션 생성 함수를 불러옵니다.
import {deleteBucket} from "./redux/modules/bucket";

const Detail = (props) => {
  const dispatch = useDispatch();
  
  // 스토어에서 상태값 가져오기
  const bucket_list = useSelector((state) => state.bucket.list);
  // url 파라미터에서 인덱스 가져오기
  let bucket_index = parseInt(props.match.params.index);

  return (
    <div>
      <h1>{bucket_list[bucket_index]}</h1>
      <button onClick={() => {
        dispatch(deleteBucket(bucket_index)); // delete type Action
      }}>삭제하기</button>
    </div>
  );
};
...
// App.js
<Route
  path="/"
  exact
  render={(props) => (
    <BucketList
      // list={props.bucket_list} // 자식 컴포넌트에서도 store로 불러주면 되기때문에 더 이상 props로 bucket_list를 보내주지 않아도 됨.
      history={this.props.history}
      />
  )}
/>

3강 과제

profile
빠굥

0개의 댓글