LoginSignup
1
1

More than 1 year has passed since last update.

【React Native】Container Componentパターンについて

Posted at

はじめに

React(またはReact Native)のデザインパターンについて勉強したので、一つずつ整理することにしました。
今回はContainer Componentパターンについてまとめました。

Container Componentパターン

Container Componentパターンとは、ロジック(およびState管理やライフサイクル)部分をContainerコンポーネント、表示(見た目)部分をPresentationalコンポーネントに分割して管理するルールのことをいいます。

役割を各コンポーネントに分離してあげることでコードの保守性を高めることができ、結果として、開発者の生産性の向上やプロジェクトの参入障壁の低減に寄与します。

また、State管理をContainerコンポーネントでまとめて行っている(Presentationalコンポーネントにpropsを渡すだけ)ことから、ロジックに対するテストも簡単になります。

実装例

ログインフォームであるApp.jsxにContainer Componentパターンを適用し、新しくサインインボタンを追加していきます。

App.jsx
export const App = () => {
  const InitialUserInfo ={
    isCurrentUser: false,
    user: '',
    password: '',
    email: ''
  };

  const [userInfo, setUserInfo] = useState(InitialUserInfo)

  return (
    <View style={styles.container}>
      <Text style={styles.label}>username</Text>
      <TextInput
        style={styles.input}
        onChangeText={user => setUserInfo({ user })}
        value={userInfo.user}
      />
      <Text style={styles.label}>password</Text>
      <TextInput
        style={styles.input}
        onChangeText={password => setUserInfo({ password })}
        value={userInfo.password}
      />
      {userInfo.isCurrentUser ? (
        // ログインフォーム
        <TouchableOpacity style={styles.fancyButton}>
          <Text style={styles.label}>Log in</Text>
        </TouchableOpacity>
      ) : (
        // サインアップフォーム
        <React.Fragment>
          <Text style={styles.label}>email</Text>
          <TextInput
            style={styles.input}
            onChangeText={email => setUserInfo({ email })}
            value={userInfo.email}
          />
          <TouchableOpacity style={styles.fancyButton}>
            <Text style={styles.label}>Sign up</Text>
          </TouchableOpacity>
        </React.Fragment>
      )}
    </View>
  );
}

まずはApp.jsxの構造を整理します。

Log in, Sign upボタン部分(<TouchableOpacity />)やusername, password, emailの入力フォーム部分(<Text /> <TextInput />)をみてみると、構造がよく似ていることがわかります。
そこで、共通化できそうな箇所をPresentationalコンポーネントとして、以下のようにまとめます。
FancyButtonFancyInputはどちらもStateをもたず、上位のContainerコンポーネントから引数で渡されます。

const FancyButton = ({ text, value, item, setItem }) => {
  return (
    <TouchableOpacity 
      style={styles.fancyButton}
      onPress={() => setItem([item], value)}
    >
      <Text style={styles.label}>{text}</Text>
    </TouchableOpacity>
  )
}

const FancyInput = ({ value, item, setItem }) => {
  return (
    <React.Fragment>
      <Text style={styles.label}>{item}</Text>
      <TextInput
        style={styles.input}
        onChangeText={value => setItem([item], value)}
        value={value}
      />
      <TouchableOpacity style={styles.fancyButton}>
        <Text style={styles.label}>Sign up</Text>
      </TouchableOpacity>
    </React.Fragment>
  )
}

次にロジック部分をContainerコンポーネントContainerとしてまとめます。
Presentationalコンポーネント内で状態更新を同じように行うために、keyとvalueでプロパティを指定して更新するsetItemを作成します。

  const setItem = (key, value) => {
    setUserInfo({ [key]: value })
  }

新しく追加するSign inボタンは、FancyButtonに必要な引数を与えてあげるだけで作成されます。

          <FancyButton 
            text={'Sign in'} 
            value={userInfo.isCurrentUser ? false : true}
            item={'isCurrentUser'}
            setItem={setItem}
          />

最後にContainerをルートのAppでラッピングしてあげれば完成です。

App.jsx
const Container = () => {
  const InitialUserInfo ={
    isCurrentUser: false,
    user: '',
    password: '',
    email: ''
  };

  const [userInfo, setUserInfo] = useState(InitialUserInfo)

  const setItem = (key, value) => {
    setUserInfo({ [key]: value })
  }

  return (
    <View style={styles.container}>
      <FancyInput 
        value={userInfo.user} 
        item={'user'}  
        setItem={setItem} 
      />
      <FancyInput 
        value={userInfo.password} 
        item={'password'}  
        setItem={setItem} 
      />
      {userInfo.isCurrentUser ? (
        // ログインフォーム
        <FancyButton text={'Log in'} />
      ) : (
        // サインアップフォーム
        <React.Fragment>
          <FancyInput 
            value={userInfo.email} 
            item={'email'}  
            setItem={setItem} 
          />
          <FancyButton text={'Sign up'} />
          <FancyButton 
            text={'Sign in'} 
            value={userInfo.isCurrentUser ? false : true}
            item={'isCurrentUser'}
            setItem={setItem}
          />
        </React.Fragment>
      )}
    </View>
  );
}

export const App = () => {
  return <Container />
}

参考資料

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1