리액트 컴포넌트, 속성, 상태

리액트 기초지식

  1. 컴포넌트(Component) : 정해진 요소를 사용하여 만든 화면의 일부분
  1. 상태(State) : 컴포넌트에서 데이터를 유지하고 관리하기 위한 방법 = 데이터
  1. 속성(Props) : 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 방식 = 데이터 전달
  1. useEffect : 화면에 컴포넌트가 그려지면 처음 실행해야 하는 함수들을 모아두는 곳
  • Component에 있는 Props가 데이터를 전달 → Component 안의 State가 데이터를 관리

 

컴포넌트(Component)

  • UI 요소, 화면의 모든 부분

 

  • Card.js
//비구조 할당 방식으로 넘긴 속성 데이터를 꺼내 사용함
export default function Card({content}) {
    return (<View style={styles.card}>
        <Image style={styles.cardImage} source={{uri:content.image}}/>
        <View style={styles.cardText}>
          <Text style={styles.cardTitle} numberOfLines={1}>{content.title}</Text>
          <Text style={styles.cardDesc} numberOfLines={3}>{content.desc}</Text>
          <Text style={styles.cardDate}>{content.date}</Text>
        </View>
      </View>)
}
  • 비구조 할당 방식 이용

 

  • MainPage.js
import Card from '../components/Card';

...

<View style={styles.cardContainer}>
	{/* 하나의 카드 영역을 나타내는 View */}
  {
		 tip.map((content,i)=>{
	     return (<Card content={content} key={i}/>)
     })
  }    
</View>
  • MainPage.js에서 Card.js로 content라는 데이터를 넘겼다.

 

속성(Props)

  • 컴포넌트에 데이터를 전달하는 것
  • key와 value의 형태 (ex. content={content})
  • 컴포넌트에 대해 map으로 반복문 → 반드시 인덱스(i)를 key={i}로 속성 전달

 

상태(useState)와 useEffect

  • 컴포넌트마다 보유, 관리하는 데이터를 상태라고 한다.
  • 리액트에서 상태(state) → useState로 생성, setState로 변경 가능

useState

  • 리액트에서 화면은 데이터에 따라 변경되고, 상태(state)로 관리되는 데이터가 변경되면 화면이 바뀐다.
  • UI = component(state)

useEffect

  • 화면이 로딩되고 가장 먼저 실행되는 함수
useEffect(()=>{
	화면이 그려지고 나서 가장 먼저 실행돼야 할 코드
},[])
  • 데이터를 준비(데이터를 받고 상태(state)에 반영)할 때 사용
  • 화면이 그려진다 → useEffect가 데이터를 준비 → 상태 데이터가 업데이트 되었으니 다시 화면이 그려진다

 

const [state, setState] = useState([])

useEffect(()=>{
	setState(data)
},[])
  • state : 컴포넌트에서 관리될 상태 데이터를 담고 있는 변수
  • setState : state를 변경시킬 때 사용해야하는 함수
  • useState() 안에 전달되는 것 → state 초기값
  • useEffect 함수로 data.json에서 가져온 데이터를 state에 담음

 

로딩 화면

  • useEffect는 화면이 그려지고 나서 실행되는데, 현재는 화면을 그릴 때 이용할 state에 값이 설정되어 있지 않은 상태임
  • 따라서 로딩 화면을 먼저 그린 후 → useEffect를 통해 state를 설정하고 나서 → 메인 화면을 그려야 오류가 나지 않음

 

  • Loading.js 파일 생성 후 MainPage.js를 다음과 같이 수정
import Loading from '../components/Loading';

const [state,setState] = useState([])
const [ready,setReady] = useState(true) //초기 ready : True

useEffect(()=>{
    setTimeout(()=>{
        setState(data)
        setReady(false)
    },1000) //1000 = 1초
 },[])
  • 상태는 여러개 만들어도 된다.
  • setTimeout으로 1초 뒤에 상태 관리에 들어가게 함

 

return ready ? <Loading/> :  (
    <ScrollView style={styles.container}>
      <Text style={styles.title}>나만의 꿀팁</Text>
			 <Text style={styles.weather}>오늘의 날씨: {todayWeather + '°C ' + todayCondition} </Text>
      <Image style={styles.mainImage} source={main}/>
      <ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
        <TouchableOpacity style={styles.middleButton01}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton02}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton03}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton04}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
      </ScrollView>
      <View style={styles.cardContainer}>
         {
          tip.map((content,i)=>{
            return (<Card content={content} key={i}/>)
          })
        }
      </View>
    </ScrollView>
  );
  • 삼항연산자를 이용해서 ready가 true이면 로딩 화면을, false이면 메인 화면을 그린다.
  • 실행 순서
    1. 초기 ready 상태는 true여서 로딩 화면을 먼저 그림
    1. useEffect가 실행돼서 1초 뒤에 state 값을 설정, ready를 false로 변경
    1. ready 상태가 변경되었으니 자동으로 retrun ready ? ... 가 실행됨
    1. false이므로 메인 화면이 그려짐

 

카테고리 기능

MainPage.js

const [state,setState] = useState([]) //전체 리스트
const [cateState,setCateState] = useState([]) //카테고리 선택에 따른 리스트
const [ready,setReady] = useState(true)

useEffect(()=>{
	setTimeout(()=>{
		let tip = data.tip;
		setState(tip)
		setCateState(tip) //처음 로딩 시 전체 리스트로 초기화
		setReady(false)
	},1000)
},[])
  • 선택한 카테고리에 따라 보여줄 리스트를 저장하는 cateState
  • useEffect에서 cateState를 tip (전체 데이터) 으로 초기화

 

<ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
	<TouchableOpacity style={styles.middleButtonAll} onPress={()=>{category('전체보기')}}><Text style={styles.middleButtonTextAll}>전체보기</Text></TouchableOpacity>
	<TouchableOpacity style={styles.middleButton01} onPress={()=>{category('생활')}}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
	<TouchableOpacity style={styles.middleButton02} onPress={()=>{category('재테크')}}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
	<TouchableOpacity style={styles.middleButton03} onPress={()=>{category('반려견')}}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
	<TouchableOpacity style={styles.middleButton04} onPress={()=>{category('꿀팁 찜')}}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
</ScrollView>
  • 카테고리 버튼마다 onPress 함수를 설정함
  • category 함수에 카테고리 이름을 넘겨주도록 함

 

const category = (cate) => {
	if(cate == "전체보기"){
		//전체보기면 원래 꿀팁 데이터를 담고 있는 상태값으로 다시 초기화
		setCateState(state)
		}else{ //전체(state)에서 선택한 카테고리 데이터만 추출
			setCateState(state.filter((d)=>{
			return d.category == cate //true일때 cateSate에 넣는다
		}))
	}
}
  • category 함수에는 cate(카테고리 이름) 파라미터가 있음
  • 전체보기를 클릭했다면 cateState를 전체 데이터(state)로 변경
  • 그 외 카테고리 버튼을 클릭했다면 전체 데이터(state) 중 해당 카테고리에 속하는 데이터만 cateState에 저장 (filter 함수 이용)

 


 

스택네비게이터

네비게이션

  • 컴포넌트들을 페이지화 시켜서 해당 페이지끼리 이동을 가능하게 하는 라이브러리

 

도구 설치

yarn add @react-navigation/native
expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view

 

스택네비게이션

  • 컴포넌트에 페이지 기능 부여, 컴포넌트에서 컴포넌트로 이동할 수 있도록 해준다.
  • Stack.Screen : 페이지
  • Stack.Navigator : 책갈피 (모든 페이지를 등록시킬 곳)

 

설치

yarn add @react-navigation/stack

 

적용하기

  • StackNavigator.js
import { createStackNavigator } from '@react-navigation/stack';
//페이지로 만들 컴포넌트 불러오기
import DetailPage from '../pages/DetailPage';
import MainPage from '../pages/MainPage';

const Stack = createStackNavigator();
const StackNavigator = () =>{
	return (
		<Stack.Navigator
			screenOptions={{
				headerStyle: {
				backgroundColor: "black",
				borderBottomColor: "black",
				shadowColor: "black",
				height:100
			},
			headerTintColor: "#FFFFFF",
			headerBackTitleVisible: false
		}}
	  >
			//페이지로 만들 컴포넌트
	    <Stack.Screen name="MainPage" component={MainPage}/>
	    <Stack.Screen name="DetailPage" component={DetailPage}/>
    </Stack.Navigator>
	)
}

export default StackNavigator;

 

  • App.js
import React from 'react';
import { StatusBar } from 'expo-status-bar';
import {NavigationContainer} from '@react-navigation/native';
import StackNavigator from './navigation/StackNavigator'

export default function App() {
	console.disableYellowBox = true;

  return ( 
  <NavigationContainer>
    <StatusBar style="black" />
    <StackNavigator/>
  </NavigationContainer>);
}
  • 앱의 가장 최상위 컴포넌트인 App.js에 네비게이션 기능을 달아준 것
  • NavigationContainer 태그로 StatusBar(상태바)과 StackNavigator(직접 만든 스택네비게이션)을 감싼다.

 

💡
Stack.screen에 등록된 모든 페이지 컴포넌트들은 navigationroute라는 딕셔너리(객체)를 속성으로 넘겨받아 사용할 수 있다!
navigation.setOptions({
   title:'나만의 꿀팁'
})
navigation.navigate("DetailPage")
navigation.navigate("DetailPage",{
  title: title
})

const { title} = route.params; //비구조 할당방식
  • navigation
    1. setOptions : 제목, 헤더 옵션 스타일 등 바꿀 수 있음
    1. navigate : 해당 페이지로 이동하는 함수 (StackNavigator.js의 Stack.screen name 속성), 두번째 매개변수로 딕셔너리 데이터 전달
  • route : navigate로 인해 전달받은 데이터를 받는 딕셔너리

 


 

공유하기 기능

  • React Native 자체에서 제공하는 기능이다.
import { Share } from "react-native";

export default function DetailPage({navigation,route}) {
		...
    const share = () => {
        Share.share({
            message:`${tip.title} \n\n ${tip.desc} \n\n ${tip.image}`,
        });
    }
    return (
        <ScrollView style={styles.container}>
            <Image style={styles.image} source={{uri:tip.image}}/>
            <View style={styles.textContainer}>
                <Text style={styles.title}>{tip.title}</Text>
                <Text style={styles.desc}>{tip.desc}</Text>
                <View style={styles.buttonGroup}>
                    <TouchableOpacity style={styles.button} onPress={()=>popup()}><Text style={styles.buttonText}>팁 찜하기</Text></TouchableOpacity>
                    <TouchableOpacity style={styles.button} onPress={()=>share()}><Text style={styles.buttonText}>팁 공유하기</Text></TouchableOpacity>
                </View>
            </View>
        </ScrollView>
    )
}
  • share 함수를 이용해 공유할 때 전달할 제목, 설명, 이미지 등을 설정할 수 있음

 


 

외부 링크 클릭 이벤트

도구 설치

expo install expo-linking

 

적용하기

import * as Linking from 'expo-linking';

const link = () => {
	Linking.openURL("https://spartacodingclub.kr")
}

<TouchableOpacity style={styles.button} onPress={()=>link()}><Text style={styles.buttonText}>외부 링크</Text></TouchableOpacity>

 


리액트를 기반으로 하는 리액트 네이티브를 공부하고 있기 때문에

리액트의 기초 지식인 컴포넌트/속성/상태에 대해 공부했다.

상태의 개념이 처음 접했을 때 헷갈렸는데, 상태의 변화가 일어날 때 화면을 새로 그린다는 것에 유의하자.

스택네비게이터를 이용해 여러 페이지간의 이동을 구현하는 방법을 알게 되어 좀더 앱다운 앱을 만들 수 있게 되었다.

그 외 공유하기 및 외부 링크 클릭 이벤트를 구현하는 방법을 익혀서 추후에 앱을 출시하게 된다면 유용하게 사용할 수 있을 것 같다!

스택네비게이션의 이용을 여러 번 복습해서 익숙해질때까지 공부해야겠다.

 

카드를 클릭하여 해당 내용의 상세 페이지로 이동한 모습

 

이번 주 숙제 : 어바웃 페이지에서 외부 링크 열기, 꿀팁찜 페이지 만들어서 네비게이터에 연결하기

+ Recent posts