디자인은... 재능이 영 없나봅니다..
오늘은 아직 뉴비인 제가 할 수 있는게 별로 없기에 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를 꿰뚫어보는 날이 얼른 왔으면 좋겠네요
오늘은 코드가 있습니다.
사실상 난이도는 틱택토가 훨씬 어려운 수준이기에 틱택토에 비해서도 코드가 길지 않습니다.
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의 상관관계는 전혀 없나 싶기도 했습니다.
해당 파일을 작성하며 생각했던 것은 다음과 같습니다.
React useState -> 동일하게 불변성을 지켜주는 코드를 작성하자. (RN도 마찬가지가 아닐까 합니다.)
ES6에서 parameter default 값을 할당해주던 것처럼 type을 정해줄 수 있다.
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 파일입니다.
마찬가지로 생각도 이어가겠습니다.
TS & RN 조합에서는 function component 사용 시 props도 type을 정해주어야 한다.
우선은 React.FC 타입으로 function component를 정의할 경우 무조건 props의 타입까지 함께 정의되어야 합니다. React.FC<CustomType or interface>
이걸 몰랐다가 children?
을 수도 없이 봤습니다. 아마도 제네릭을 내장하고 있는 방식인 것 같은데, 자세한건 라이브러리를 까봐야 알 것 같습니다.
함수를 정의하는게 약간 이게 맞나? 싶을 정도로 뭔가 많이 적힌다.
React를 기준으로 component 분리를 했는데, RN도 마찬가지일까?
등의 생각들이 있었습니다.
확실히 아직 TS도 RN도 익숙하지 않지만, 더 열심히 해서 firebase 연동, 호스팅까지 이번주 안으로는 꼭 해봐야겠다는 생각이 있습니다.
그럼 다음 글은 더 나아진 모습으로 돌아오겠습니다.
감사합니다.