리액트 네이티브 - Todo 앱 만들기

아현·2021년 3월 13일
1
post-thumbnail

1. 폴더 만들어주기

여기서는 expo를 사용하도록 하겠다.


> expo init todoApp
> cd todoApp
> code .


//npm start

2. todo 앱 레이아웃 작성하기

<View>
   <Heading />
   <Input />
   <TodoList />
   <Button />
   <TabBar />
</View>



3. todo 앱 코드 작성하기


import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View, ScrollView } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <ScrollView keyboardShouldPersistTaps ='always' style={styles.content}>


      </ScrollView>
      
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5'
  },

  content: {
    flex: 1,
    paddingTop: 60

  }
});



  • ScrollView 컴포넌트

    • 플랫폼을 감싸는 것으로 스크롤이 가능한 View 컴포넌트이다.

    • 속성 keyboardShouldPersistTaps ='always' 는 키보드가 열려있으면 닫아서 UI가 onPress 이벤트를 모두 처리하게 한다.

    • flex: 1은 스타일 값으로 해당 컴포넌트가 상위 ㅋ넌테이너 영역 전체를 채우도록 해준다.



초기 state 지정하기 (App.js)


class App extends Component() {

  constructor(){
    super()
    this.state={
      inputValue: '',
      todos: [],
      type: 'All'
    }
	
    ...
  }
    
  ...
  • 먼저 App을 함수형에서 클래스로 변경해주었다.

  • 나중에 필요한 몇 가지를 초기 state로 지정했다.

    • 여러 todo를 다루어야하기 때문에 배열이 필요하기에 이름은 todos로 설정하였다.

    • todo들을 추가하는 TextInput의 현재 state를 저장하는 값이 필요하기에 이름은 inputValue로 설정하였다.

    • 현재 보고 있는 todo의 타입을 저장할 값(All, Current, Active)이 필요하기에 이름은 type으로 설정하였다.



Heading 컴포넌트 만들기(Heading.js)

import React from 'react';
import {View, Text, StyleSheet} from 'react-native';

const Heading = () => {
  //{}아닌 return 사용하지 않고 ()로 감싸도 된다.
    return(
        <View style={styles.header}>
            <Text style={styles.headerText}>
                todos
            </Text>

        </View>
    )

}

const styles = StyleSheet.create({

    header: {
        marginTop: 80
    },

    headerText: {
        textAlign: 'center',
        fontSize: 72,
        color: 'rgba(175, 47, 47, 0.25)',
        fontWeight: '100'

    }
})

export default Heading



  • 이 컴포넌트는 stateless 컴포넌트이다.

이제 App.js에 Heading 컴포넌트를 가져와 사용해보자


import React,{Component} from 'react';
import {View, ScrollView, StyleSheet} from 'react-native';
import Heading from './Heading';

class App extends Component{
   constructor(){
    super()
    this.state={
      inputValue: '',
      todos: [],
      type: 'All'
   }
     
  render(){
    return(
      <View style={styles.container}>
        <ScrollView  keyboardShouldPersistTaps='always' style={styles.content}>
          <Heading />
        </ScrollView>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  constainer: {
    flex:1,
    backgroundColor: '#f5f5f5'
  },
  content: {
    flex: 1,
    paddingTop: 60
  }

})

export default App




TextInput 컴포넌트 만들기 (Input.js)



import React from 'react';
import {View, TextInput, StyleSheet} from 'react-native';

const Input = () => (
    <View>
        <TextInput  
            style={StyleSheet.inputContainer}
            placeholder='What nees to be done?'
            placeholderTextColor='#CACACA'
            selectionColor='#666666'/>
        
    </View>

)

const styles = StyleSheet.create({
    inputContainer:{
        marginLeft: 20,
        marginRight: 20,
        shadowOpacity: 0.2,
        shadowRadius: 3,
        shadowColor:'#000000',
    },
    input:{
        height:60,
        backgroundColor: '#ffffff',
        paddingLeft: 10,
        paddingRight: 10

    }

})

export default Input
  • TextInput

    • HTML의 input 태그와 유사하다.

    • placeholder는 사용자가 텍스트를 입력하기 전에 보여주는 텍스트를 지정한다.

    • placeholderTextColor는 플레이스 홀더의 텍스트 스타일을 지정한다.

    • selectionColor는 TextInput의 커서 스타일을 지정한다.



다음으로는 TextInput의 값을 가져오기 위해서 기능을 연결하고, App 컴포넌트의 상태에 이를 저장한다.

그리고 App.js 파일로 가서 inputChange라는 새로운 메서드를 생성자 아래와 render 메서드 위 사이에 추가한다.

이 메서드는 전달받은 inputValue의 값을 이용해서 state를 갱신하고 제대로 동작하는 지를 console.log()를 이용해서 출력한다.



inputChange 메서드 작성하기


import React,{Component} from 'react';
import {View, ScrollView, StyleSheet} from 'react-native';
import Heading from './Heading';
import Input from './Input';

class App extends Component{
  constructor(){
    super()
    this.state={
      inputValue: '',
      todos: [],
      type: 'All'
    }

  inputChange(inputValue){
    console.log('Input Value:', inputValue)
    this.setState({inputValue})
  }

  render(){

    const {inputValue} = this.state
    
    return(
      <View style={styles.container}>
        <ScrollView  keyboardShouldPersistTaps='always' style={styles.content}>
          <Heading />
          <Input 
            inputValue={inputValue}
            inputChange={(text) => this.inputChange(text)}/>
        </ScrollView>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  constainer: {
    flex:1,
    backgroundColor: '#f5f5f5'
  },
  content: {
    flex: 1,
    paddingTop: 60
  }

})

export default App



  • Input 컴포넌트를 App.js 파일로 가져와 TextInput에 메서드를 연결한다.

  • TextInput은 Input 컴포넌트에 prop으로 전달되고, state를 저장한 inputValue는 Input 컴포넌트의 prop으로 전달된다.

  • inputChange 메서드는 인수가 하나로 TextInput의 값을 전달한다.
    이 메서드는 TextInput에서 반환된 값으로 inputValue를 갱신한다.



inputChange와 inputValue를 TextInput에 추가하기 (Input.js)

...

const Input = ({inputValue, inputChange}) => (
    <View>
        <TextInput  
            value={inputValue}
            style={StyleSheet.inputContainer}
            placeholder='What nees to be done?'
            placeholderTextColor='#CACACA'
            selectionColor='#666666'
            onChangeText={inputChange}/>
        
    </View>
  
...

  • TextInput 컴포넌트를 새로운 inputChange 메서드와 inputValue prop으로 갱신한다.

  • 상태를 유지하지 않는 컴포넌트를 만들면서 속성(prop)으로 전달된 inputValue와 inputChange를 구조분해할당 처리를 한다.

  • TextInput의 값이 변경되면 inputChange 메서드가 호출되고, 이 값은 부모 컴포넌트로 전달되어 inputValue의 상태를 지정하게 된다.

    • TextInput의 값이 inputValue로 지정되면 나중에 TextInput을 제어하고 재 지정할 수 있게 된다.
  • onChangeText 메서드 는 TextInput의 컴포넌트의 값이 변경될 때마다 이 메서드가 호출되고, TextInput의 값이 전달된다.


inputValue의 값이 state로 저장되어 있으므로 이제 todo 목록에 항목을 추가하는 버튼을 만들어야 한다.

전처럼 버튼에 바인딩되는 메서드를 작성하는데 이 메서드는 새로운 todo를 생성자에서 정의했던 todos 배열에 추가해 준다.

이 메서드의 이름은 submitTodo로 하고 inputChange 메서드 뒤면서 render 메서드 앞에 두도록 한다.



submitTodo 메서드 추가하기 (App.js)


import React,{Component} from 'react';
import {View, ScrollView, StyleSheet} from 'react-native';
import Heading from './Heading';
import Input from './Input';

class App extends Component{
  constructor(){
    super()
    this.state={
      inputValue: '',
      todos: [],
      type: 'All'
    }
  }

  inputChange(inputValue){
    console.log('Input Value:', inputValue)
    this.setState({inputValue})
  }

  submitTodo(){
    //inputValue가 비어있는지 확인
    if(this.state.inputValue.match(/^\s*$/)){
      return
    }

    //inputValue가 비어있지 않으면 todo 변수를 생성하고 title, todoINdex, complete 객체를 할당
    const todo = {
      title: this.state.inputValue,
      todoIndex,
      complete: false
    }

    //인덱스 증가
    todoIndex++

    //새로운 todo를 기존 배열에 추가
    const todos = [...this.state.todos, todo]

    //todo의 state를 지정해 this.state.todos의 갱신된 배열과 일치하게 만들고 inputValue를 빈 문자열로 재지정
    this.setState({todos, inputValue:''}, () => {
      console.log('State: ', this.state)
    })

    
  }

  render(){

    const {inputValue} = this.state

    return(
      <View style={styles.container}>
        <ScrollView  keyboardShouldPersistTaps='always' style={styles.content}>
          <Heading />
          <Input 
            inputValue={inputValue}
            inputChange={(text) => this.inputChange(text)} />
        </ScrollView>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  constainer: {
    flex:1,
    backgroundColor: '#f5f5f5'
  },
  content: {
    flex: 1,
    paddingTop: 60
  }

})

export default App



마지막으로 import 문 아래면서 App.js 파일의 맨 위에 todoIndex 변수를 생성한다.


...

import Input from './Input';

//전역으로 선언
let todoIndex = 0

class App extends Component{
  
  ...



submitTodo 메서드를 작성했으므로 Button.js 파일을 만들어서 submitTodo 메서드와 연결버튼이 작동되게 한다.



Button 컴포넌트 만들기 (Button.js)


import React from 'react'
import { View, Text, StyleSheet, TouchableHighlight } from 'react-native'

const Button = ({ submitTodo }) => (
  <View style={styles.buttonContainer}>
    <TouchableHighlight underlayColor='#efefef' style={styles.button} onPress={submitTodo}>
      <Text style={styles.submit}>
        Submit
      </Text>
    </TouchableHighlight>
  </View>
)

const styles = StyleSheet.create({
  buttonContainer: {
    alignItems: 'flex-end'
  },
  button: {
    height: 50,
    paddingLeft: 20,
    paddingRight: 20,
    backgroundColor: '#ffffff',
    width: 200,
    marginRight: 20,
    marginTop: 15,
    borderWidth: 1,
    borderColor: 'rgba(0,0,0,.1)',
    justifyContent: 'center',
    alignItems: 'center'
  },
  submit: {
    color: '#666666',
    fontWeight: '600'
  }
})

export default Button



  • TouchableHighlight

    • 리액트 네이티브에서 버튼을 만들 때의 한 가지 방법

    • 뷰들을 감싸는 게 가능하고 이들 뷰가 터치 이벤트에 적절히 대응하게 해준다.

    • 버튼을 누르면 디폴트 backgroundColor는 지정된 underlayColor로 바뀌며, prop으로 지정한다.

      • underlayColor를 지정하지 않으면 디폴트는 검은색이다.
    • TouchableHighlight는 자식 컴포넌트 하나만을 다룬다

      • 여기서는 Text 컴포넌트를 전달하고 있다.
      • 여러 컴포넌트를 다루고자 하는 경우 이들 컴포넌트를 View 하나로 먼저 감싸고, 이렇게 한 View를 TouchableHighlight의 자식으로 전달하면 된다.



    Button 컴포넌트 연결 (App.js)


import Button from './Button'

let todoIndex = 0
...

  constructor(){
      ...
      this.submitTodo = this.submitTodo.bind(this)
  }
  
  
  ...
        <Input inputValue={inputValue}
               inputChange={(text) => this.inputChange(text)}/>
        <Button submitTodo={this.submitTodo} />
       </ScrollView>
      </View>
    )
  }
}


...



  • Button 컴포넌트를 가져와 render 메서드 내 Input 컴포넌트 아래에 두었다.

  • this.submitTodo라는 prop으로 Button에 submitTodo를 전달하였다.

이제 앱을 새로고치고 todo를 추가하면 TextInput은 지워지고 앱의 state는 콘솔에 기록되면서 새로운 todo가 todos배열에 보이게 된다.


todo 배열에 todo들을 추가했으므로 화면에 랜더링해야 한다.
렌더링을 하려면 TodoList와 Todo를 만들어야 한다.

TodoList는 todo 목록을 렌더링하고 각각의 todo에 대해서는 Todo 컴포넌트를 사용한다.



Todo 컴포넌트 만들기 (Todo.js)


import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
import TodoButton from './TodoButton'

const Todo = ({ todo }) => (
  <View style={styles.todoContainer}>
    <Text style={styles.todoText}>
      {todo.title}
    </Text>
  </View>
)

const styles = StyleSheet.create({
  todoContainer: {
    marginLeft: 20,
    marginRight: 20,
    backgroundColor: '#ffffff',
    borderTopWidth: 1,
    borderRightWidth: 1,
    borderLeftWidth: 1,
    borderColor: '#ededed',
    paddingLeft: 14,
    paddingTop: 7,
    paddingBottom: 7,
    shadowOpacity: 0.2,
    shadowRadius: 3,
    shadowColor: '#000000',
    shadowOffset: { width: 2, height: 2 },
    flexDirection: 'row',
    alignItems: 'center'
  },
  todoText: {
    fontSize: 17
  },
})

export default Todo

  • Todo 컴포넌트는 지금 속성으로 todo객체 하나를 가져와 Text 컴포넌트에 제목으로 렌더링했다.

계속해서 TodoList 컴포넌트를 만들자



TodoList 컴포넌트 만들기 (TodoList.js)


import React from 'react'
import { View } from 'react-native'
import Todo from './Todo'

const TodoList = ({ todos }) => {
 
  todos = todos.map((todo, i) => {
    return (
      <Todo
        key={i}
        todo={todo} />
    )
  })

  return (
    <View>
      {todos}
    </View>
  )
}

export default TodoList


  • TodoList 컴포넌트는 현재 하나의 속성인 todos 배열을 갖는다.

    • todos 배열을 매핑해 각각의 todo에 대해 새로운 Todo 컴포넌트를 만들고 각 Todo 컴포넌트에 속성으로 todo 객체를 전달했다.
  • 키를 지정해 각 컴포넌트에 해당하는 키로 todo 항목의 인덱스를 전달했다.

  • key 속성은 가상 DOM으로 diff가 구해지면서 바뀌게 되는 항목을 리액트가 식별하는 데 도움이 된다.

    • key 속성을 그대로 두면 리액트는 경고 메시지를 출력한다.

마지막으로 할 작업은 TodoList 컴포넌트를 App.js 파일로 가져와 속성으로 todos를 전달하는 것이다.


TodoList 가져오기 (App.js)


import TodoList from './TodoList'

...
 
class App extends Component {
 ...
 
 render () {
    const { todos, inputValue } = this.state
    
    return(){
       <View
        style={styles.container}>
        <ScrollView
          keyboardShouldPersistTaps='always'
          style={styles.content}>
          <Heading />
          <Input
            inputValue={inputValue}
            inputChange={(text) => this.inputChange(text)} />
          <TodoList todos={todos} />
          <Button
            submitTodo={this.submitTodo} />
        </ScrollView>
      </View>
    )
  }
 }
 




다음 작업은 todo를 complete로 표시하면서 todo를 제거하는 것이다.
App.js 파일을 열어 submitTodo 메서드 아래에 toggleComplete와 deleteTodo 메서드를 작성한다.

toggleComplete 메서드는 todo가 완료되었는지를 전환해주고, deleteTodo 메서드는 todo를 제거해준다.



toggleComplete와 deleteTodo 메서드 추가하기 (App.js)


...
  constructor(){
       ...
       this.toggleComplete = this.toggleComplete.bind(this)
       this.deleteTodo = this.deleteTodo.bind(this)
  }
   ...
    
  deleteTodo (todoIndex) {
    let { todos } = this.state
    todos = this.state.todos.filter((todo) => {
      return todo.todoIndex !== todoIndex
    })
    this.setState({ todos })
  }

  toggleComplete (todoIndex) {
    let { todos } = this.state
    todos.forEach((todo) => {
      if (todo.todoIndex === todoIndex) {
        todo.complete = !todo.complete
      }
    })
    this.setState({ todos })
  }
    
  • deleteTodo는 todoIndex를 인수로 하며, todos를 필터링해 전달된 인덱스의 todo를 제외한 모든 todo들을 반환, 그런 다음 state를 나머지 todos로 재지정

  • toggleComplete는 todoIndex를 인수로하며, 주어진 인덱스의 todo를 만날 때까지 todos를 반복, complete 부울 값을 현재와 반대되게 바꾸고 todos의 state를 재지정

이들 메서드를 연결하려면 todo에 전달할 버튼 컴포넌트를 만들어야 한다.



TodoButton.js 파일 작성하기


import React from 'react'
import { Text, TouchableHighlight, StyleSheet } from 'react-native'

const TodoButtton = ({ onPress, complete, name }) => (
  <TouchableHighlight onPress={onPress} underlayColor='#efefef' style={styles.button}>
    <Text style={[ styles.text, complete ? styles.complete : null, name === 'Delete' ? styles.deleteButton : null ]}>
      {name}
    </Text>
  </TouchableHighlight>
)

const styles = StyleSheet.create({
  button: {
    alignSelf: 'flex-end',
    padding: 7,
    borderColor: '#ededed',
    borderWidth: 1,
    borderRadius: 4,
    marginRight: 5
  },
  text: {
    color: '#666666'
  },
  complete: {
    color: 'green',
    fontWeight: 'bold'
  },
  deleteButton: {
    color: 'rgba(175, 47, 47, 1)'
  }
})

export default TodoButton

  • compelete가 true인지 확인하고, 스타일 적용
  • name이 'Delete'인지 확인하고 , 그런 경우 스타일 적용



toggleComplete와 deleteTodo를 TodoList 속성으로 전달하기 (App.js)


   render(){
    ...
 
    <TodoList
            type={type}
            toggleComplete={this.toggleComplete}
            deleteTodo={this.deleteTodo}
            todos={todos} />
    
     <Button submitTodo={this.submitTodo} />
    
    ...
   }

   



toggleComplete와 deleteTodo를 ToDo에 속성으로 전달하기

...

const TodoList = ({ type, todos, deleteTodo, toggleComplete }) => {

  todos = todos.map((todo, i) => {
    return (
      <Todo
        key={i}
        deleteTodo={deleteTodo}
        toggleComplete={toggleComplete}
        todo={todo} />
    )
  })

...
 

마지막으로 Todo.js 파일을 열어서 Todo 컴포넌트를 갱신해 새로운 TodoButton 컴포넌트와 버트 ㄴ컨테이너의 스타일을 적용


Todo.js를 갱신해 TodoButton과 기능을 적용하기 (Todo.js)

import TodoButton from './TodoButton';

...

const Todo = ({ todo, toggleComplete, deleteTodo }) => (
   <View style={styles.todoContainer}>
    <Text style={styles.todoText}>
      {todo.title}
    </Text>
    <View style={styles.buttons}>
      <TodoButton name='Done' complete={todo.complete} onPress={() => toggleComplete(todo.todoIndex)} />
      <TodoButton name='Delete' onPress={() => deleteTodo(todo.todoIndex)} />
    </View>
  </View>
)
 
...

const styles = StyleSheet.create({
 ...
   buttons: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'flex-end',
    alignItems: 'center'
  },
 ...
})

  • Done과 Delete라는 두 개의 TodoButton을 추가하였습니다. 다음으로 TodoButton.js 파일에 정의한 onPress로 호출할 메서드로 toggleComplete와 deleteTodo를 전달하였습니다.

마지막 과정에서는 탭 바 필터를 만들겠습니다. 이 필터는 todo 목록 전체를 표시해주거나, 완료되었거나 작업중인 todo만을 선택적으로 표시해 줍니다.


생성자에서 앱을 처음 만들 때 state인 type 변수를 'All'로 지정했는데 이제 setType이라는 메서드를 만드는데 이 메서드는 인수로 type을 가지며 state인 type을 갱신해 줍니다.

이 메서드는 App.js 파일의 toggleComplete 메서드 아래에 둡니다.


setType 메서드 추가하기(App.js)


...
 constructor(){
   ...
   this.setType = this.setType.bind(this)
 }
 ...
 
 setType(type){
    this.setState({type})
 }

다음으로는 TabBar와 TabBarItem 컴포넌트를 만들어야 합니다.


TabBar 컴포넌트 만들기 (TabBar.js)


import React from 'react'
import { View, StyleSheet } from 'react-native'
import TabBarItem from './TabBarItem'

const TabBar = ({ setType, type }) => (
  <View style={styles.container}>
    <TabBarItem
      type={type}
      title='All'
      setType={() => setType('All')} />
    <TabBarItem
      type={type}
      border
      title='Active'
      setType={() => setType('Active')} />
    <TabBarItem
      type={type}
      border
      title='Complete'
      setType={() => setType('Complete')} />
  </View>
)

const styles = StyleSheet.create({
  container: {
    height: 70,
    flexDirection: 'row',
    borderTopWidth: 1,
    borderTopColor: '#dddddd'
  }
})

export default TabBar
  • 이 컴포넌트는 setType과 type을 속성으로 가집니다.

    • 두 속성은 모두 App 컴포넌트에서 전달받도록 합니다.
  • 아직 정의하지 않은 TabBarItem 컴포넌트를 가져옵니다.

    • TabBarItem 컴포넌트는 속성이 셋으로 title, type, setType입니다.

    • border라는 속성은 Boolean 값을 가지며, 지정하면 왼쪽 테두리 스타일을 추가해 줍니다.



TabBarItem 컴포넌트 만들기 (TabBarItem.js)


import React from 'react'
import { Text, TouchableHighlight, StyleSheet } from 'react-native'

const TabBarItem = ({ border, title, selected, setType, type }) => (
  <TouchableHighlight
    underlayColor='#efefef'
    onPress={setType}
    style={[
      styles.item, selected ? styles.selected : null,
      border ? styles.border : null,
      type === title ? styles.selected : null ]}>
    <Text style={[ styles.itemText, type === title ? styles.bold : null ]}>
      {title}
    </Text>
  </TouchableHighlight>
)

const styles = StyleSheet.create({
  item: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
  border: {
    borderLeftWidth: 1,
    borderLeftColor: '#dddddd'
  },
  itemText: {
    color: '#777777',
    fontSize: 16
  },
  selected: {
    backgroundColor: '#ffffff'
  },
  bold: {
    fontWeight: 'bold'
  }
})

export default TabBarItem

  • TouchableHighlight 컴포넌트에서 몇 가지 속성들을 확인하고서 prop에 근거해 스타일을 지정합니다.

    • selected가 true면 스타일을 styles.seleted로

    • border가 true면 스타일을 styles.border로 지정

    • type이 title과 같으면 스타일을 styles.seleted로 지정합니다.

    • Text 컴포넌트에서 type이 title과 같으닞 확인하고, 그렇다면 스타일을 styles.bold로 지정


TabBar를 구현하기 위해 app/App.js 파일을 열어 TabBar 컴포넌트를 가져와 지정합니다.
그리고 this.state를 비구조화하는 일환으로 render 메서드에 type을 가져옵니다.



TabBar 컴포넌트 구현하기 (App.js)

...
import TabBar from '.TabBar'
class App extends Component{
...
 render(){
     const { todos, inputValue, type } = this.state
 return (
      <View
        style={styles.container}>
        <ScrollView
          keyboardShouldPersistTaps='always'
          style={styles.content}>
          <Heading />
          <Input
            inputValue={inputValue}
            inputChange={(text) => this.inputChange(text)} />
          <TodoList
            type={type}
            toggleComplete={this.toggleComplete}
            deleteTodo={this.deleteTodo}
            todos={todos} />
          <Button
            submitTodo={this.submitTodo} />
        </ScrollView>
        <TabBar
          type={type}
          setType={this.setType} />
      </View>
    )
  }
}
  • TabBar 컴포넌트를 가져온 다음 state에서 type을 구조할당하고 새로운 TabBar 컴포넌트에 전달하고 TodoList 컴포넌트에도 전달합니다.

    • todo들을 필터링할 때 이 type 변수를 사용합니다.
    • 또한 setType 메서드를 prop으로 TabBar 컴포넌트에 전달합니다.

마지막으로 TodoList 컴포넌트를 열고 필터를 추가해서, 선택한 탭에 따라 지금 복원하려는 타입의 todo들만 반환하도록 해야 합니다.

TodoList.js 파일을 열고 prop 중 type을 비구조화하고 return 문 앞에 getVisibleTodos 메서드를 추가하도록 합니다.



TodoList 컴포넌트 갱신하기(TodoList.js)


...

const TodoList = ({ type, todos, deleteTodo, toggleComplete }) => {
  const getVisibleTodos = (todos, type) => {
    switch (type) {
      case 'All':
        return todos
      case 'Complete':
        return todos.filter((t) => t.complete)
      case 'Active':
        return todos.filter((t) => !t.complete)
    }
  }

  todos = getVisibleTodos(todos, type)

  todos = todos.map((todo, i) => {


...

  • switch 문을 사용해 현재 type이 무엇으로 지정되어 있는지 확인합니다.

    • 'All'로 지정되어 있으면 todo 목록 전체를 반환
    • 'Complete'로 지정되어 있으면 todo들을 필터해서 완료된 todo만 반환
    • 'Active'로 지정되어있으면 작업 중인 todo만 반환
  • 다음으로 todos 변수를 getVisivleTodos 메서드가 반환하는 값으로 지정




Git

profile
Studying Computer Science

1개의 댓글

comment-user-thumbnail
2022년 11월 22일

잘 보고 잘 따라해 배워갑니다.
덕분에 React Native 가 어떻게 움직이는지 대략 알게 되었어요.

답글 달기