Atomic Designを意識したリファクタリングができた(気がする)

最近、きれいなコードを書くことを意識しています。
[個人開発でとりあえずリリースまではできた](https://qiita.com/wafuwafu13/items/3c1ad74e8984d67e0a95)のですが、コードがまだまだ汚いので、少しづつきれいにしていく日々を過ごしています。
今回は、流れてきたAtmic Designの記事を読み、実践してみたので一部をメモしておきます。

- [フロントエンドのコンポーネント設計に立ち向かう](https://qiita.com/seya/items/8814e905693f00cdade2)
- [Atomic Designってデザイナーには難しくない！？という話](https://qiita.com/rhirayamaaan/items/7f990e146ec01f2e7e08)


#### やりたいこと

<img width="605" alt="スクリーンショット 2020-04-15 10.45.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/8f24a42e-d4d7-c819-827a-4736c8cca2c0.png">

「人口」、「身長・体重」の``楕円形``と、その下の``四角``をそれぞれAtomとし、再利用できるようにする。


#### リファクタリング前のコード

```javascript:Menu.js

import React, { Component } from 'react';
...

class Menu extends Component{
    
    render(){
        const {navigate} = this.props.navigation
        const styles = StyleSheet.create({
            ...
            explanation:{
                marginTop: hp('2%'),
                fontSize: wp('2%'),
                color: '#4B4B4B',
            },
            populationTitle:{
                flex: 1,
                justifyContent: 'center',
      　　　　　　alignItems: 'center',
                borderWidth: wp('0.3%'),
                borderRadius: 100,
                borderColor: '#EEF5F6',
                backgroundColor: '#99D260',
            },
            populationIcon:{
                flex: 6,
                justifyContent: 'center',
      　　　　　　alignItems: 'center',
                borderWidth: wp('0.7%'),
                borderRadius: 10,
                borderColor: '#EEF5F6',
                backgroundColor: '#99D260',
            },
            bodyTitle:{
                flex: 1,
                justifyContent: 'center',
      　　　　　　alignItems: 'center',
                borderWidth: wp('0.3%'),
                borderRadius: 100,
                borderColor: '#EEF5F6',
                backgroundColor: '#56A7E2',
            },
            bodyIcon:{
                flex: 6,
                justifyContent: 'center',
      　　　　　　alignItems: 'center',
                borderWidth: wp('0.7%'),
                borderRadius: 10,
                borderColor: '#EEF5F6',
                backgroundColor: '#56A7E2',
            }
        })

       ...
        return(
           
                .....
                            <Animatable.View animation="bounce" style={styles.menu}>
                                <View style={styles.populationTitle}>
                                    <Text style={styles.menuTitle}>
                                        人 口
                                    </Text>
                                </View>
                                <TouchableOpacity 
                                  style={styles.populationIcon}
                                  onPress={ () => goto('PopulationMenu')}
                                >
                                    <Icon name="child" size={wp('10%')} color="#4C8469"/>
                                    <Text style={styles.explanation}>
                                        5つのグラフ
                                    </Text>
                                </TouchableOpacity>
                            </Animatable.View>

                            <Animatable.View animation="bounce" style={styles.menu}>
                                <View style={styles.bodyTitle}>
                                    <Text style={styles.menuTitle}>
                                        身長・体重
                                    </Text>
                                </View>
                                <TouchableOpacity
                                  style={styles.bodyIcon}
                                  onPress={ () => goto('BodyMenu')}
                                >
                                    <Icon name="ruler" size={wp('10%')} color="#324CA8"/>
                                    <Text style={styles.explanation}>
                                        2つのグラフ
                                    </Text>
                                </TouchableOpacity>
                            </Animatable.View>
            .....
        )
    }
}

export default Menu;
```

見ての通り、``テキスト``、``背景色``、``onPressの挙動``、``アイコン``ぐらいしか変わらないのに、コードの重複がとても多い。



#### リファクタリング後のコード

```javascript:Menu.js
import React, { Component } from 'react';
...

class Menu extends Component{
    
    render(){

        const {navigate} = this.props.navigation;

        return(
            ...
                            <Animatable.View animation="bounce" style={styles.menu}>
                                <MenuItemTitle item="population">
                                    人 口
                                </MenuItemTitle>
                                <MenuItem item="population" onPress={ () => goto('PopulationMenu') }>
                                    5つのグラフ
                                </MenuItem>
                            </Animatable.View>

                            <Animatable.View animation="bounce" style={styles.menu}>
                                <MenuItemTitle item="body">
                                    身長・体重
                                </MenuItemTitle>
                                <MenuItem item="body" onPress={ () => goto('BodyMenu') }>
                                    2つのグラフ
                                </MenuItem>
                            </Animatable.View>
                            <Animatable.View animation="bounce" style={styles.menu}>
                               
        )
    }
}

...
export default Menu;

```

```javascript:MenuTitle.js
import React, { Component } from 'react';
...


class MenuTitle extends Component {

    render(){

        const { item }  = this.props
        const bgColor = {
            "population": '#99D260',
            "body": '#56A7E2',
        }

        return(
            <View style={[styles.title, { backgroundColor: bgColor[item]}]}>
                <Text style={styles.titleText}>
                    { this.props.children }
                </Text>
            </View>
        )
    }
}

export default MenuTitle;

const styles = StyleSheet.create({
    title:{
        flex: 1,
        justifyContent: 'center',
　　　　 alignItems: 'center',
        borderWidth: wp('0.3%'),
        borderRadius: 100,
        borderColor: '#EEF5F6',
    },
    titleText:{
        fontSize: wp('2.5%'),
        color: '#4B4B4B',
    },
})
```

```javascript:MenuItem.js
import React, { Component } from 'react';
...


class MenuItem extends Component {

    render(){

        const { item, onPress } = this.props
        const bgColor = {
            "population": '#99D260',
            "body": '#56A7E2',
        }
        const icon = {
            "population": <Icon name="child" size={wp('10%')} color="#4C8469"/>,
            "body": <Icon name="ruler" size={wp('10%')} color="#324CA8"/>,
        }
        
        return(
            <TouchableOpacity 
              style={[styles.icon, {backgroundColor: bgColor[item]}]}
              onPress={onPress}
            >
                { icon[item] }
                <Text style={styles.explanation}>
                    { this.props.children }
                </Text>
            </TouchableOpacity>

        )
    }
}

export default MenuItem


const styles = StyleSheet.create({
    explanation:{
        marginTop: hp('2%'),
        fontSize: wp('2%'),
        color: '#4B4B4B',
    },
    icon:{
        flex: 6,
        justifyContent: 'center',
        alignItems: 'center',
        borderWidth: wp('0.7%'),
        borderRadius: 10,
        borderColor: '#EEF5F6',
        backgroundColor: '#99D260',
    }
})
```

- ``楕円形``を担うファイルとその下の``四角``を担うファイルを作成し、分割した。
- ``テキスト``は、this.props.childrenで処理した。
- propsでitemの種類を受け取り、if文で分岐すると冗長になるので、オブジェクトで管理している値と照らし合わせることで、挙動を変えた。
- ``アイコン``は、jsxをオブジェクトのvalueで管理して、jsxの中で代入した。
- ``onPressの挙動``は、propsで受け取ったものを発火するようにした。
- ``背景色``は、styleを配列で定義し、インデックス0に共通のスタイル、インデックス1に更新したいスタイルを定義した。

##### 感想

コードの重複をかなり抑えられて気持ちよかった。
まだなんかできる気はする。
このくらいコードを書く前に気づけたらなーと思った。