본문 바로가기
Front-end/React\RN

리액트네이티브, 목표추가 모달, 추가, 지우기, 이미지

by javapp 자바앱 2023. 6. 14.
728x90

 

메인, 새 목표 추가 , 추가완료

  • 새 목표 추가 버튼 클릭
  • 모달로 입력 창이 나타나고 목표추가
  • 추가 완료시 모달창이 사라지고 다시 메인 창이 보여짐
  • 화면 크기 이상 늘어날 시 스크롤 기능 작동

 

App.js

import { useState } from 'react';
import { StyleSheet, View, FlatList, Button} from 'react-native';
import { StatusBar } from 'expo-status-bar';

import GoalItem from './components/GoalItem';
import GoalInput from './components/GoalInput';

export default function App() {
  const [modalIsVisible, setModalIsVisible] = useState(false)
  const [courseGoals, setCourseGoals] = useState([]);

  function startAddGoalHandler(){
    setModalIsVisible(true);
  }

  function endAddGoalHandler(){
    setModalIsVisible(false);
  }

  function addGoalHandler(enteredGoalText) {
    setCourseGoals((currentCourseGoals) => [
      ...currentCourseGoals,
      { text: enteredGoalText, id: Math.random().toString() },
    ]);
    endAddGoalHandler();
  }

  function deleteGoalHandler(id) {
    setCourseGoals((currentCourseGoals) => {
      return currentCourseGoals.filter((goal) => goal.id !== id);
    });
  }

  return (
    <>
    <StatusBar style='light'/>
    <View style={styles.appContainer}>
      <Button title='새 목표 추가' color="#a065ec" onPress={startAddGoalHandler}/>
      <GoalInput visible={modalIsVisible} onAddGoal={addGoalHandler} onCancel={endAddGoalHandler} />
      <View style={styles.goalsContainer}>
        <FlatList
          data={courseGoals}
          renderItem={(itemData) => {
            return (
              <GoalItem
                text={itemData.item.text}
                id={itemData.item.id}
                onDeleteItem={deleteGoalHandler}
              />
            );
          }}
          keyExtractor={(item, index) => {
            return item.id;
          }}
          alwaysBounceVertical={false}
        />
      </View>
    </View>
    </>
  );
}

const styles = StyleSheet.create({
  appContainer: {
    flex: 1,
    paddingTop: 50,
    paddingHorizontal: 16,
    backgroundColor: '#1e085a'
  },
  goalsContainer: {
    flex: 5,
  },
});

 

GoalInput 컴포넌트를 모달로 표현

visible 속성 modalsVisible 값을 넘긴다.

  <GoalInput visible={modalIsVisible} onAddGoal={addGoalHandler} onCancel={endAddGoalHandler} />

 

useState로 변수 제어

const [modalIsVisible, setModalIsVisible] = useState(false)

 

추가(onAddGoal), 취소(onCancel) 를 할 때 실행.

  function addGoalHandler(enteredGoalText) {
    setCourseGoals((currentCourseGoals) => [
      ...currentCourseGoals,
      { text: enteredGoalText, id: Math.random().toString() },
    ]);
    endAddGoalHandler();
  }
  function endAddGoalHandler(){
    setModalIsVisible(false);
  }

리액트에서 데이터 추가 코드 작성시 권장하는 방법

...currentCourseGoals 이전 값 복사 

 

새 목표 추가 버튼 클릭시 모달창 visible

<Button title='새 목표 추가' color="#a065ec" onPress={startAddGoalHandler}/>
  function startAddGoalHandler(){
    setModalIsVisible(true);
  }

 

FlatList에서 renderItem 으로 아이템 생성

          renderItem={(itemData) => {
            return (
              <GoalItem
                text={itemData.item.text}
                id={itemData.item.id}
                onDeleteItem={deleteGoalHandler}
              />
            );
          }}
  const [courseGoals, setCourseGoals] = useState([]);
  function deleteGoalHandler(id) {
    setCourseGoals((currentCourseGoals) => {
      return currentCourseGoals.filter((goal) => goal.id !== id);
    });
  }

람다표현과 filter으로 화면상 데이터 삭제

 

 


 

 

GoalInput.js

import { useState } from 'react';
import { View, TextInput, Button, StyleSheet, Modal , Image } from 'react-native';

function GoalInput(props) {
  const [enteredGoalText, setEnteredGoalText] = useState('');

  function goalInputHandler(enteredText) {
    setEnteredGoalText(enteredText);
  }

  function addGoalHandler() {
    props.onAddGoal(enteredGoalText);
    setEnteredGoalText('');
  }

  return (
    <Modal visible={props.visible} animationType=''> // 모달로 전체를 감싸준다.
        <View style={styles.inputContainer}>
          <Image style={styles.image} source={require('../assets/images/icons8.png')} />
          <TextInput
            style={styles.textInput}
            placeholder="Your course goal!"
            onChangeText={goalInputHandler}
            value={enteredGoalText}
          />
          <View style={styles.buttonContainer}>  
            <View style={styles.button}>
              <Button title="Add Goal" onPress={addGoalHandler} color="#b180f0" />

            </View>
            <View style={styles.button}>
              <Button title='취소' onPress={props.onCancel} color="#f31282"/>

            </View>
          </View>
        </View>
    </Modal>
  );
}

export default GoalInput;

const styles = StyleSheet.create({
  inputContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 16,
    backgroundColor: '#311b6b'
  },
  textInput: {
    borderWidth: 1,
    borderColor: '#e4d0ff',
    backgroundColor: '#e4d0ff',
    color: '#120438',
    borderRadius: 6,
    width: '80%',
    marginRight: 8,
    padding: 12,
  },
  buttonContainer: {
    marginTop: 10,
    flexDirection: 'row'
  },
  button: {
    width: '30%',
    marginHorizontal: 8,
  },
  image: {
    width: 100,
    height: 100,
    margin: 20
  }
});

 

우선 props를 통해 전달된 속성들을 받아올 수 있다.

 

모달의 visible값을 props로 받아온 값으로 넣어준다.

<Modal visible={props.visible} animationType=''>

 

이미지 컴포넌트

<Image style={styles.image} source={require('../assets/images/icons8.png')} />

require 함수 파라미터로 상대경로를 넣어준다.

 

 

 

 

 

텍스트 input 컴포넌트

        <TextInput
          style={styles.textInput}
          placeholder="Your course goal!"
          onChangeText={goalInputHandler}
          value={enteredGoalText}
        />

값이 변할 때 마다 해당 메소드핸들러를 실행 (goalInputHandler)

  const [enteredGoalText, setEnteredGoalText] = useState('');
 
  function goalInputHandler(enteredText) {
    setEnteredGoalText(enteredText);
  }

 

버튼 컴포넌트

        <View style={styles.buttonContainer}>  
          <View style={styles.button}>
            <Button title="Add Goal" onPress={addGoalHandler} color="#b180f0" />
          </View>
          <View style={styles.button}>
            <Button title='취소' onPress={props.onCancel} color="#f31282"/>
          </View>
        </View>

전체 스타일을 잡기 위해 View로 한번 더 묶음

 

추가 버튼 클릭시 해당 메소드 실행

  function addGoalHandler() {
    props.onAddGoal(enteredGoalText);
    setEnteredGoalText('');
  }

실질적으로는 App에서 정의된 메소드로 기능 수행

 

 

 

GoalItem.js

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

function GoalItem(props) {
  return (
    <View style={styles.goalItem}>
      <Pressable
        android_ripple={{ color: '#210644' }}
        onPress={props.onDeleteItem.bind(this, props.id)}
        style={({ pressed }) => pressed && styles.pressedItem}
      >
        <Text style={styles.goalText}>{props.text}</Text>
      </Pressable>
    </View>
  );
}

export default GoalItem;

const styles = StyleSheet.create({
  goalItem: {
    margin: 8,
    borderRadius: 6,
    backgroundColor: '#5e0acc',
  },
  pressedItem: {
    opacity: 0.5,
  },
  goalText: {
    color: 'white',
    padding: 8,
  },
});

 

반복으로 생성된 GoalItem 컴포넌트

  return (
    <View style={styles.goalItem}>
      <Pressable
        android_ripple={{ color: '#210644' }}
        onPress={props.onDeleteItem.bind(this, props.id)}
        style={({ pressed }) => pressed && styles.pressedItem}
      >
        <Text style={styles.goalText}>{props.text}</Text>
      </Pressable>
    </View>
  );

Pressable 컴포넌트로 누를 수 있는 뷰로 만들었다.

android 효과를 준다.

 

컴포넌트 눌렸을 때 실행되는 메소드

onPress={props.onDeleteItem.bind(this, props.id)}

this 값을 현재 컨텍스트로 설정

 

 

해당 요소가 눌렸을 때 스타일 설정

style={({ pressed }) => pressed && styles.pressedItem}

 

 

 

 

 

댓글