Expo+Redux(+firebase)로 로그인 폼② ~Redux 도입~

소개



Expo+Redux(+firebase)로 로그인 폼 ① ~ 개요 · Expo 준비 ~ 의 계속입니다.

이번 기사에서는 Redux를 사용하여 우선 간단한 카운터를 만들어 갑니다.
차례차례의 기사로 로그인 화면과 화면 천이를 구현해 나갈 예정입니다.

현재 디렉토리 구성


root/
 ├ App.js
 ├ package.json

src 디렉토리 만들기



root에 여러가지 넣어 가면 이해하기 어려워지므로, src 디렉토리를 만들어 App을 옮깁시다
Expo는 기본적으로 root/App.js를 로드하기 때문에 이를 변경하기 위해 node_modules/expo/AppEntry.js를 편집합니다.

/node_modules/expo/AppEntry.js
-- import App from '../../App';
++ import App from '../../src/App';

그런 다음 root/App.js를 삭제하고 root/src/App.tsx를 작성합니다.

Redux 도입



우선 App.tsx에 redux를 넣습니다.
우선 최소한의 구성으로, 1 파일에 정리하고 있습니다

src/App.tsx
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { createStore, combineReducers } from 'redux'
import { Provider, connect } from 'react-redux'

// reducer
function counter(state, action) {
  if (action.type === 'undefined') {
    return null
  }

  switch(action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return null
  }
}

// store
const store = createStore(combineReducers({ count: counter }))

// component
function Counter({ count, dispatch }) {
  return (
    <View style={styles.container}>
      <Text style={styles.paragraph}>{count}</Text>
      <Button
        title='Increment'
        onPress={() => dispatch({ type: 'INCREMENT' })}
      />
      <Button
        title='DECREMENT'
        onPress={() => dispatch({ type: 'DECREMENT' })}
      />
    </View>
  )
}

// container
const CounterContainer = connect(state => ({ count: state.count }))(Counter)

export default function App() {
  return (
    <Provider store={store}>
      <CounterContainer />
    </Provider>
  )
}

// スタイル
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
  paragraph: {
    margin: 24,
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
  },
})

Reducer



App.tsx
// reducer
function counter(state, action) {
  if (action.type === 'undefined') {
    return null
  }

  switch(action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return null
  }
}

action과 현재의 state를 받고 새로운 state를 돌려주는 함수입니다.
action이 없거나 swich 문의 default에서 null을 반환하지 않으면 오류가 발생하므로주의가 필요합니다.

스토어



root/App.tsx
// store
const store = createStore(combineReducers({ count: counter }))

Reducer가 향후 늘어나는 것을 근거로, combineReducer로 정리해 둡니다.

Component/Container



App.tsx
// component
function Counter({ count, dispatch }) {
  return (
    <View style={styles.container}>
      <Text style={styles.paragraph}>{count}</Text>
      <Button
        title='Increment'
        onPress={() => dispatch({ type: 'INCREMENT' })}
      />
      <Button
        title='DECREMENT'
        onPress={() => dispatch({ type: 'DECREMENT' })}
      />
    </View>
  )
}

// container
const CounterContainer = connect(state => ({ count: state.count }))(Counter)

화면에 표시하는 부분입니다.
버튼을 누르면 type이 INCREMENT/DECREMENT인 action이 dispatch됩니다.
버튼을 눌렀을 때의 콜백 함수는, 본래는 Container측에 써야 한다고는 생각합니다만, 코드가 조금 까다롭게 되기 때문에, 그것은 나중에 하려고 합니다.



App.tsx
export default function App() {
  return (
    <Provider store={store}>
      <CounterContainer />
    </Provider>
  )
}

ReactNative는 로 둘러싸고 store를 전달하면 Redux와 연결할 수 있습니다.

여기까지 실행하면 이런 느낌


다음 번



이번은 최저 구성의 redux를 짜 보았으므로, 다음번은 Ducks 패턴을 의식해 파일을 나누어 가고 싶습니다.
그런 다음 ReactNativeDebugger도 사용할 수 있습니다.
Expo+Redux(+firebase)로 로그인 폼③~파일 정리·Debugger~

좋은 웹페이지 즐겨찾기