본문 바로가기
App/React Native

[React Native] 2 component, StyleSheet

by Wordbe 2020. 5. 7.
728x90

React Native 002 - Basics

Core Components

  • 리액트 네이티브에 빌드 → 리액트 네이티브에 의해 제공된 네이티브 UI 위젯으로 번역(translation)

Styling

  • CSS는 없음
  • Inline Styles, StyleSheet Objects (CSS 문법 기반이지만, 속성, 특징의 일부만 지원함)
  • StyleSheet Objects를 권장

 

앱 구상

사소하지만, 그럼에도 불구하고 매우 중요하고 도움되는 앱을 제작해보겠습니다.

목표를 추가하는 버튼 구성, 목표 리스트 화면에 보여지고, 한 목표를 탭하면 삭제할 수 있도록 구현


중요 컴포넌트

  • 텍스트를 작성하려면, <Text> 태그로 텍스트를 꼭 감싸주어야 함.
  • <View> : 텍스트, 이미지, 유저 인풋에 대한 응답 등에 사용되는 스크린 위의 작은 네모 상자 원소, 기본 빌딩 블락

레이아웃

  • 리액티브 RN의 대부분 컴포넌트는 style 속성을 가짐

    <View style={{padding: 30}}></View>
  • flex box

    • React Native의 컴포넌트는 기본 속성이 flex로 되어 있어 flex container를 이룹니다. 따라서 flex 관련 속성을 사용할 수 있습니다.

    • main-axis, cross-axis(main-axis와 수직)

    • flexDirection : 'row' (→), 'column' (↓), 'row-reverse' (←), 'column-reverse' (↑)

      web 의 flex는 'row'가 기본이지만,

      app 에서의 flex는 'column'이 기본입니다.

    • justfiyContent (main-axis기준 정렬), alignItems (cross-axis기준 정렬)

      'center', 'stretch', 'space-between', 'space-around' 등

    • flex container 안의 flex item 들에서 사용할 수 있는 속성

      • flex: 1 → flex contatiner 의 크기에 따라 flex item의 크기가 결정됨
      • 여러 flex item들이 있고, flex: 1, flex: 2 이렇게 있으면 각각 영역을 1/3, 2/3 씩 차지
  • <View> 의 기본 크기는 안에있는 텍스트, 이미지 등의 크기에 비례


Inline vs StyleSheet

  • lnline 스타일은 태그안에 스타일 속성을 기록하므로, 복잡한 스타일은 가독성이 좋지 않습니다.

  • StyleSheet 객체를 통해, 객체 형태로 각각의 스타일을 생성할 수 있습니다.

    import { StyleSheet } from 'react-native';
    
    export default function App() {
        return (
            <View style={styles.style1}>
                <View style={styles.style2}>
                </View>
            </View>
        );
    }
    
    const styles = StyleSheet.create({
        style1: {
            padding: 30
        },
        style2: {
            color: white;
        }
    })

    StyleSheet 객체는 각 스타일 문법의 검증(validation)과 잠재적인 성능 향상을 제공합니다.


상태, 이벤트 관리(State, Event)

  • useState 이용, 상태와 상태 업데이트를 관리

    import React, { useState } from 'react';
    import { StyleSheet, Text, View, TextInput, Button } from 'react-native';
    
    export default function App() {
      const [enteredGoal, setEnteredGoal] = useState('');
      const [courseGoals, setCourseGoals] = useState([]);
    
      const goalInputHandler = (enteredText) => {
        setEnteredGoal(enteredText);
      };
    
      const addGoalHandler = () => {
        setCourseGoals(currentGoals => [...currentGoals, enteredGoal]);
      };
    
      return (
        <View style={styles.container}>
          <Text>Wordbe's App</Text>
    
          <View style={styles.innterView}>
            <TextInput
              placeholder="Course Goal"
              style={styles.textInputStyle}
              onChangeText={goalInputHandler}
              value={enteredGoal}
            />
            <Button title="Add" onPress={addGoalHandler} />
          </View>
          <View>
            {courseGoals.map((goal) => <Text key={goal}>{goal}</Text>)}
          </View>
        </View>
      );
    }
  • 위 예제의 경우, enteredGoal 에 입력된 데이터를 coursGoals 에 추가하며, 이를 map 메소드를 이용해서 출력

  • map 메소드에서 Text 태그로 출력할 때, key 입력을 해주면, 효율적으로 리스트를 관리할 수 있습니다.


Styling

  • <Text> 태그는 style 속성을 많이 지원하지 않으므로, <View> 로 덮어씌운 후 스타일링을 적용하는 것이 좋습니다.
  • key는 루트 컴포넌트에 적용시켜주어야 합니다.
  • RN styling 속성에는 CSS와 다르게 marginVertical 이 있습니다.
<View>
    {courseGoals.map((goal) =>
    <View key={goal} style={styles.listItem}>
        <Text>{goal}</Text>
    </View>)}
</View>

 


ScrollView

  • web 페이지 화면이 꽉차면 자동으로 스크롤이 생기지만, 앱은 설정해주어야 합니다.

    import { ScrollView } from 'react-native';
  • 화면을 넘는 컴포넌트를 스크롤뷰로 감싸주면 됩니다.

 


FlatList

  • ScrollView의 경우, 보이지 않는 요소가 화면에 없더라도 모두 렌더링 되기 때문에 앱을 느리게 할 수 있습니다.
  • FlatList 는 필요한 것만 렌더링하게 하여 스크롤링을 최적화합니다.
<View>
    {courseGoals.map((goal) =>
    <View key={goal} style={styles.listItem}>
        <Text>{goal}</Text>
    </View>)}
</View>

위 코드를 아래와 같이 변경할 수 있습니다.

<FlatList
    data={courseGoals}
    renderItem={(itemData) => {
        return (
            <View style={styles.listItem}>
                <Text>{itemData.item.value}</Text>
            </View>
        );
    }}
/>
  • 단, 데이터는 {key, value} 형식의 자바스크립트 객체여야 합니다.

  • data : 리스트 형식의 데이터 안에 key, value 값을 가진 객체가 있어야 합니다.

  • rennderItem : { } 안에 함수를 넣으면, data 의 인자를 하나씩 받아 함수에 넣어 반환합니다.

  • FlatList는 key 라는 요소를 기본 키로 인식합니다. 이를 커스터마이징 하려면 keyExtractor를 사용합니다.

    예) key 대신 id 를 사용할 경우

    const addGoalHandler = () => {
        setCourseGoals((currentGoals) => [
            ...currentGoals,
            { id: Math.random().toString(), value: enteredGoal }
        ]);
    };
    ...
    <FlatList
        keyExtractor={(item, index) => item.id}
        data={courseGoals}
        renderItem={(itemData) => {
            return (
                <View style={styles.listItem}>
                    <Text>{itemData.item.value}</Text>
                </View>
            );
        }}
    />

 


컴포넌트 분리하기

App.js에 몰려있던 내용들을 유지/보수 및 가독성을 위해 components/GoalInput.js , components/GoalItem.js 에 분산시켜 봅시다.

App.js

import React, { useState } from 'react';
import {
  StyleSheet,
  Text,
  View,
  TextInput,
  Button,
  ScrollView,
  FlatList
} from 'react-native';
import GoalItem from './components/GoalItem';
import GoalInput from './components/GoalInput';

export default function App() {
  const [courseGoals, setCourseGoals] = useState([]);
  const addGoalHandler = (goalTitle) => {
    setCourseGoals((currentGoals) => [
      ...currentGoals,
      { id: Math.random().toString(), value: goalTitle }
    ]);
  };

  return (
    <View style={styles.container}>
      <Text>Wordbe's App</Text>

      <GoalInput onAddGoal={addGoalHandler} />

      <FlatList
        keyExtractor={(item, index) => item.id}
        data={courseGoals}
        renderItem={(itemData) => {
          return <GoalItem title={itemData.item.value} />;
        }}
      />
    </View>
  );
}

components/GoalInput.js

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

function GoalInput(props) {
  const [enteredGoal, setEnteredGoal] = useState('');
  const goalInputHandler = (enteredText) => {
    setEnteredGoal(enteredText);
  };

  return (
    <View style={styles.innerView}>
      <TextInput
        placeholder="Course Goal"
        style={styles.textInputStyle}
        onChangeText={goalInputHandler}
        value={enteredGoal}
      />
      <Button title="Add" onPress={props.onAddGoal.bind(this, enteredGoal)} />
    </View>
  );
}

const styles = StyleSheet.create({
  textInputStyle: {
    marginRight: 5,
    borderColor: 'black',
    borderWidth: 1
  },
  innerView: {
    flexDirection: 'row',
    padding: 30
  }
});

export default GoalInput;
  • App.js 에서 보면 GoalInput 컴포넌트의 props 로 addGoalHandler 함수를 전달해주었습니다.
  • GoalInput.js 에서는 받아온 함수를 Button의 인자로 사용하고 있습니다. addGoalHandler 함수는 props.onAddGoal로 받아와지는데, 이 함수는 이전 객체 currentGoals와 enteredGoal이라는 인수가 필요합니다.
  • 이 때, bind 함수에 this 인자를 넣어서 업데이트 사항이 반영된 객체가 계속 올 수 있도록 반영합니다.

components/GoalItem.js

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

function GoalItem(props) {
  return (
    <View style={styles.listItem}>
      <Text>{props.title}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  listItem: {
    pading: 10,
    marginVertical: 3,
    backgroundColor: '#ccc',
    borderColor: 'black',
    borderWidth: 1
  }
});

export default GoalItem;
728x90

댓글