(TIL) 3. React-Native : To Do List

김동우·2021년 9월 1일
0

React-Native

목록 보기
3/17

디자인은... 재능이 영 없나봅니다..

1.

오늘은 아직 뉴비인 제가 할 수 있는게 별로 없기에 tutorial에 자주 등장하는 To Do List를 만들어봤습니다.

이것저것 공식문서도 참고하고, TS도 익숙해지기 위해 열심히 사용해봤습니다.

React Native는 비교적 제약이 많은 component로 구성되는 것 같습니다.

그냥 막 때려넣은 다음, "props 입니다." 해도 실행되던 React와 달리 만들어져있는 component 프레임을 가져다 속을 채우는 방식이라 그런 것 같습니다.

그래서 props로 줄 수 있는 key와 value가 정해져있습니다.

TS를 사용해서 그런 것인지는 모르겠습니다만, 아무 props나 전달하더라도 런타임 에러가 발생하는 것은 아닌듯 합니다.
어차피 component 내부 로직에서 전달받은 props를 사용하지 않으니 문제가 없을 것 같기도 합니다.

또한 생각보다 React Native에는 다양한 component들이 존재하는데, 공식문서가 꽤 친절하게 나열해주었는데도 오늘 tutorial에 사용해본건 4~5개 쯤 되었던 것 같습니다.

모든 component를 꿰뚫어보는 날이 얼른 왔으면 좋겠네요

2.

오늘은 코드가 있습니다.

사실상 난이도는 틱택토가 훨씬 어려운 수준이기에 틱택토에 비해서도 코드가 길지 않습니다.

import React, {useState} from 'react';
import {
  Platform,
  SafeAreaView,
  StyleSheet,
  View,
  Text,
  TextInput,
  Button,
} from 'react-native';
import {ToDos} from './ToDos';

export type ToDosProps = {
  toDos: IToDo[];
  setToDos: Function;
};

export interface IToDo {
  id: number;
  text: string;
  completed: boolean;
}

const handleSubmit = (
  inputValue: string,
  toDos: IToDo[],
  error: Boolean,
  setToDos: Function,
  setinputValue: Function,
) => {
  if (inputValue && !error) {
    setToDos([
      ...toDos,
      {
        id: toDos.length !== 0 ? toDos[toDos.length - 1].id + 1 : 1,
        text: inputValue,
        completed: false,
      },
    ]);
    setinputValue('');
  } else {
    setinputValue('');
  }
};

export const TodoList = () => {
  const [inputValue, setinputValue] = useState<string>('');
  const [toDos, setToDos] = useState<IToDo[]>([]);
  const [error, setError] = useState<Boolean>(false);

  const {toDoListContainer, mainTitle, inputContainer, taskInput, taskTitle} =
    styles;

  return (
    <SafeAreaView style={toDoListContainer}>
      <Text style={mainTitle}>Todo List</Text>
      <View style={inputContainer}>
        <TextInput
          style={taskInput}
          placeholder="Type your plan - by typeS"
          value={inputValue}
          onChangeText={(e: string): void => {
            setinputValue(e);
            setError(false);
          }}
          placeholderTextColor="#fff"
        />
        <Button
          color="#000"
          title="Add Task"
          onPress={() => {
            handleSubmit(inputValue, toDos, error, setToDos, setinputValue);
          }}
        />
      </View>
      <View>
        {toDos[0] && <Text style={taskTitle}>오늘 할 일</Text>}
        {toDos[0] && <ToDos toDos={toDos} setToDos={setToDos} />}
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  toDoListContainer: {
    flex: 1,
    justifyContent: 'flex-start',
    alignItems: 'stretch',
    paddingTop: Platform.OS === 'android' ? 25 : 0,
    backgroundColor: '#4b7bec',
  },

  mainTitle: {
    marginBottom: 30,
    color: '#fff',
    fontSize: 36,
    fontWeight: '300',
    textAlign: 'center',
  },

  inputContainer: {
    alignItems: 'center',
  },

  taskInput: {
    height: 80,
    color: '#fff',
    fontSize: 25,
  },

  taskTitle: {
    marginTop: 20,
    marginBottom: 25,
    marginLeft: 20,
    fontSize: 25,
  },
});

우선은 메인 component가 되는 ToDoList component입니다.

문법은 tsx가 적용된 상태이고, 어제 고민했던 union과 styles의 상관관계는 전혀 없나 싶기도 했습니다.

해당 파일을 작성하며 생각했던 것은 다음과 같습니다.

  1. React useState -> 동일하게 불변성을 지켜주는 코드를 작성하자. (RN도 마찬가지가 아닐까 합니다.)

  2. ES6에서 parameter default 값을 할당해주던 것처럼 type을 정해줄 수 있다.

  3. interface와 type을 export 하는 것은 생각보다 유용하다.

    • 문제는 관리를 어떻게 할지인데, 그렇기에 내일은 d.ts, declare module, namespace 등의 개념을 공부해볼 예정입니다.
import React from 'react';
import {StyleSheet, View, Text, TouchableOpacity} from 'react-native';
import {IToDo, ToDosProps} from './TodoList';

const handleComplete = (
  toDos: IToDo[],
  setToDos: Function,
  idx: number,
): void => {
  const newToDos: IToDo[] = [...toDos];
  if (newToDos[idx].completed) {
    newToDos.splice(idx, 1, {...newToDos[idx], completed: false});
  } else {
    newToDos.splice(idx, 1, {...newToDos[idx], completed: true});
  }
  setToDos(newToDos);
};

const deleteToDo = (toDos: IToDo[], setToDos: Function, idx: number): void => {
  const newToDos: IToDo[] = [...toDos];
  newToDos.splice(idx, 1);
  setToDos(newToDos);
};

export const ToDos: React.FC<ToDosProps> = ({toDos, setToDos}) => {
  const {taskBox, task} = styles;

  const toDoList = toDos.map((toDo: IToDo, idx: number) => {
    return (
      <View style={taskBox} key={'toDo' + toDo.id}>
        <TouchableOpacity
          onPress={() => {
            handleComplete(toDos, setToDos, idx);
          }}>
          <Text style={task}>{toDo.completed ? '✔️' : '😂'}</Text>
        </TouchableOpacity>
        <Text style={task}>{toDo.text}</Text>
        <TouchableOpacity
          onPress={() => {
            deleteToDo(toDos, setToDos, idx);
          }}>
          <Text style={task}></Text>
        </TouchableOpacity>
      </View>
    );
  });

  return <>{toDoList}</>;
};

const styles = StyleSheet.create({
  taskBox: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    marginRight: 20,
    marginBottom: 15,
    marginLeft: 20,
  },

  task: {
    fontSize: 20,
  },
});

이어서 자식 component인 ToDos.tsx 파일입니다.

마찬가지로 생각도 이어가겠습니다.

  1. TS & RN 조합에서는 function component 사용 시 props도 type을 정해주어야 한다.

    • 우선은 React.FC 타입으로 function component를 정의할 경우 무조건 props의 타입까지 함께 정의되어야 합니다. React.FC<CustomType or interface>

    • 이걸 몰랐다가 children? 을 수도 없이 봤습니다. 아마도 제네릭을 내장하고 있는 방식인 것 같은데, 자세한건 라이브러리를 까봐야 알 것 같습니다.

  2. 함수를 정의하는게 약간 이게 맞나? 싶을 정도로 뭔가 많이 적힌다.

  3. React를 기준으로 component 분리를 했는데, RN도 마찬가지일까?

등의 생각들이 있었습니다.

확실히 아직 TS도 RN도 익숙하지 않지만, 더 열심히 해서 firebase 연동, 호스팅까지 이번주 안으로는 꼭 해봐야겠다는 생각이 있습니다.

그럼 다음 글은 더 나아진 모습으로 돌아오겠습니다.

감사합니다.

0개의 댓글