반업주부의 일상 배움사

[React Native] Expo 로 To-do 앱 만들기 :: MobX 버전 본문

IT 인터넷/React Native & JS

[React Native] Expo 로 To-do 앱 만들기 :: MobX 버전

Banjubu 2020. 11. 13. 19:12
반응형

 

개발 환경

expo-cli 설치

sudo npm install -g expo-cli

 

프로젝트 생성 및 실행

expo init todo_app
cd todo_app
yarn start

# blank 또는 blank (Typescript) 선택

 

w 누르고 웹에서 실행

 

* 여기서는 상태 관리를 위해 MobX(https://mobx.js.org/) 를 이용해요.

sudo yarn add mobx mobx-react

 

UI

Store.tsx

import { observable } from 'mobx';

const store = observable({
    todos: [],
    add(newTodo) {
        this.todos.push(newTodo);
    }
});

export { store };

 

Header.tsx

import React, { Component } from 'react';
import { StyleSheet, View, TextInput, TouchableOpacity } from 'react-native'
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { store } from './Store';

class Header extends Component {
    constructor(props:any) {
        super(props);

        this.state = {
            txt: ''
        };
    }

    add = function (txt) {
        if(txt.length < 1) return;

        store.add({
            id: new Date(),
            text: txt,
            completed: false,
        });

        this.setState({
            txt: ''
        });
    }

    render() {
        return (
            <View style={styles.container}>
                <View style={styles.input}> 
                    <TextInput 
                        style={styles.inputText}
                        placeholder='Enter new todo'
                        value={this.state.txt}
                        onChangeText={(t)=>this.setState({txt:t})}
                    />
                    <TouchableOpacity onPress={()=>this.add(this.state.txt)}>
                        <MaterialCommunityIcons style={styles.addBtn} size={30} name='plus-circle' />
                    </TouchableOpacity>
                </View>
            </View>
        )
    }
}

const styles = StyleSheet.create({
    container: {
        marginLeft: 20,
        marginRight: 20,
    },
    input: {
        borderRadius: 10,
        backgroundColor: "#FFF",
        paddingLeft: 10,
        paddingRight: 10,
        height: 50,
        alignItems: "center",
        flexDirection: 'row',
        justifyContent: 'space-between',
        borderBottomColor: "#bbb",
        borderBottomWidth: StyleSheet.hairlineWidth,
    },
    inputText: {
        flex: 1,
    },
    addBtn: {
        color: '#4169E1'
    }
});

export default Header;

 

Body.tsx

import React, { Component } from 'react';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import { store } from './Store';
import { observer } from 'mobx-react';

class Body extends Component {
    constructor(props:any) {
        super(props);
    }

    check = function (data) {
        const index = store.todos.lastIndexOf(data);
        store.todos[index].completed = !store.todos[index].completed;
    }

    remove = function (data) {
        const index = store.todos.lastIndexOf(data);
        store.todos.splice(index, 1);
    }
    
    render() {
        return (
            <View style={styles.container}>
                {
                    store.todos.map((data:any) => (
                        <View style={styles.todo} key={data.id}>
                            <View style={styles.todoText}>
                                <TouchableOpacity style={styles.todoCheckbox} onPress={ ()=>this.check(data) }>
                                    {
                                        data.completed
                                        ? <MaterialCommunityIcons size={20} name='checkbox-marked-circle-outline' />
                                        : <MaterialCommunityIcons size={20} name='checkbox-blank-circle-outline' />
                                    }
                                </TouchableOpacity>
                                <Text>{data.text}</Text>
                            </View>
                            <TouchableOpacity onPress={ () => this.remove(data) }>
                                <MaterialCommunityIcons style={styles.todoDelBtn} size={30} name='delete-outline' />
                            </TouchableOpacity>
                        </View>
                    ))
                }
            </View>
        )
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        marginVertical: 5,
        marginHorizontal: 20,
        padding: 10,
        backgroundColor: "#FFF",
        borderRadius: 10,
    },
    todo: {
        flexDirection: 'row',
        alignItems: "center",
        justifyContent: 'space-between',
        height: 50,
        borderBottomColor: "#bbb",
        borderBottomWidth: StyleSheet.hairlineWidth,
    },
    todoCheckbox: {
        marginRight: 5,
    },
    todoText: {
        flexDirection: 'row',
    },
    todoDelBtn: {
        color: '#777'
    }
});

export default observer(Body);

 

App.tsx

import React, {useState} from 'react';
import { StyleSheet, View, Text, TouchableOpacity } from 'react-native';
import Header from './Header';
import Body from './Body';

export default class App extends React.Component {
  constructor(props:any) {
    super(props);
  }

  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.title}>Todo App</Text>
        <Header />
        <Body />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
    paddingTop: 50,
    backgroundColor: "#EEE",
  },
  title: {
    fontWeight: "800",
    fontSize: 30, 
    marginLeft: 20,
    marginBottom: 20,
  }
});

 

ActiveJS는 모든 것을 개별적으로 관리할 수 있다는 장점이 있고, MobX는 다 관리해주니 편하게 쓸 수 있네요.

 

ActiveJS 버전

https://banjubu.tistory.com/122

 

 

영어, 중국어 공부중이신가요?

홈스쿨 교재. 한 권으로 가족 모두 할 수 있어요!

 

한GLO 미네르바에듀 : 네이버쇼핑 스마트스토어

한글로 영어가 된다?! 한글로[한GLO]는 영어 중국어 일어 러시아어 스페인어가 됩니다!!

smartstore.naver.com

 

반응형
LIST
Comments