리액트 네이티브 학습 내용 정리

RN을 학습하기 위해 간단하게 에어비엔비 UI를 따라서 만들어보고 있는 와중에 배우며 느낀점들을 정리해보았습니다.

부모 border 밖으로 튀어나옴 현상

View 컴포넌트 에 border-radius값을 주고 그 안에 Image컴포넌트로 이미지를 채웠을 때, 생각했던 대로라면 안에있는 이미지가 부모 보더 안에 채워져야 하는데, radius가 적용되지 않고 그냥 이미지 그대로 나와있었습니다. 구조는 아래와 같습니다.

...
function ContentsCard( ) {
  return (
    <Container >
      <Image resizeMode="stretch" source={item.img} />

    </Container>
  );
}

const Container = styled.View`
  border-radius: 20px;
  margin-right: 15px;
`;

const Image = styled.Image`
  width: 100%;
  height: 300px;
`;

위와 같을 때, 그랬는데 몇가지 검색을 해보니 오버플로우 히든을 주어라 라는 내용이 있어서 적용을 해보았습니다.

const Container = styled.View`
  border-radius: 20px;
  margin-right: 15px;
  overflow: hidden;
`

또한 보더값을 줄때, 공식 문서에서 View 컴포넌트의 style Props를 보면 border-radius는 따로 없고, borderBottomEndRadius, borderBottomLeftRadius, borderBottomRightRadius 이렇게 부분적으로 되어있습니다. border-radius로 넣어줄때 적용은 되는데, 문제가 조금씩 있는것 같습니다.

failed child context type invalid 에러

flatList컴포넌트를 사용하며 개발을 하는데, 위와같은 에러가 나왔습니다. 살펴보니 FlatList의 키는 반드시 문자열 키를 가져야 한다는 내용이었습니다.

const data = [
  {
    id: 1,
    img: require('./Assets/allison.png'),
    label: '라스베이거스 댄서의 하루',
  },
  {
    id: 2,
    img: require('./Assets/aiisha.png'),
    label: '아이샤 잭슨과 함께하는 사랑,빛,치유의 저녁',
  },
  {
    id: 3,
    img: require('./Assets/merv.png'),
    label: '마술과 독심술을 즐기는 법',
  },
  {
    id: 4,
    img: require('./Assets/pedro.png'),
    label: '라이브 뮤지컬 참여하기',
  },
];

...
function Component() {
  return (
      <CardList
        data={data}
        renderItem={({ item, index }) => (
          <ContentsCard item={item} index={index} />
        )}
        keyExtractor={item => item.id}
      />

  );
}

const CardList = styled.FlatList`
`;

위와같이 각각 리스트 요소의 id값이 number 였기에, 해당 에러가 발생했습니다. 그래서 해당 number를 toString()를 사용해 string화 해주었습니다.

<CardList
  data={data}
  renderItem={({ item, index }) => <ContentsCard item={item} index={index} />}
  keyExtractor={item => item.id.toString()}
/>

virtualizedlist should never be nested inside… 에러

리액트 네이티브를 해보며 처음보는 에러들을 많이 만났는데, 위의 에러는 FlatList와 ScrollView를 중첩해서 겹쳐서 쓰지 말라는 에러 입니다. react native의 docs를 보며 컴포넌트들의 props를 확인하며 개발을 하는데, ScrollView에 있는 속성들과 FlatList에 있는 속성들을 모두 사용해야 해서 겹쳐서 썼는데, 위의 에러가 나왔습니다.

function Component() {
  return (
    <ScrollView
      decelerationRate={0}
      snapToInterval={Dimensions.get('window').width * 0.7 + 15}
      snapToAlignment="start"
      showsHorizontalScrollIndicator={false}
    >
      <FlatList
        horizontal
        data={data}
        renderItem={({ item, index }) => (
          <ContentsCard item={item} index={index} />
        )}
        keyExtractor={item => item.id.toString()}
      />
    </ScrollView>
  )
}

위와 같이 snapToInterval 같은 속성이 필요했었습니다. 그래서 에러가 생겼는데, 두 속성이 모두 필요한데 어떻게 겹쳐 쓰지 말라는거지? 라는 생각이 들었습니다. 그래서 알아보니 FlatList컴포넌트는 ScrollView컴포넌트의 속성을 상속받는다 라고 해서 ScrollView의 속성을 FlatList에 주었습니다.

function Component() {
  return (
    <FlatList
      decelerationRate={0}
      snapToInterval={Dimensions.get('window').width * 0.7 + 15}
      snapToAlignment="start"
      showsHorizontalScrollIndicator={false}
      horizontal
      data={data}
      renderItem={({ item, index }) => (
        <ContentsCard item={item} index={index} />
      )}
      keyExtractor={item => item.id.toString()}
    />
  )
}

공식문서를 다시 보니 공식문서에도 역시 상속받는다고 나와있었습니다. 다른것도 그렇지만, 리액트 네이티브는 특히 더 공식문서를 잘 확인해야 하는것 같습니다.

가로 스크롤 페이징 넘김 처리

스크롤 페이징 넘김 처리에 있어서 UI를 잡는데 꽤 어려움이 있었는데, 요령껏(?) 해결한 방법입니다. 부모 요소에서 FlatList로 가로처리를 하고 슥샥슥샥 넘기는데 화면에 한 요소가 꽉 차는것이 아니라, 애매하게 남고 스크롤 을 일정하게 주기 위한 작업입니다.

function Parent() {
  return (
    <CardList
      decelerationRate={0}
      snapToInterval={Dimensions.get('window').width * 0.7 + 15}
      snapToAlignment="start"
      showsHorizontalScrollIndicator={false}
      horizontal
      data={data}
      renderItem={({ item, index }) => <Child item={item} index={index} />}
      keyExtractor={item => item.id.toString()}
    />
  )
}
const CardList = styled.FlatList`
  padding-left: 30px;
`

부모요소의 FlatList 인데, 가로 스크롤링을 위해 horizontal속성을 주고, showsHorizontalScrollIndicator를 false로 주어서 스크롤바를 없애고, 슬라이딩 효과를 위해 snapToAlignment, snapToInterval, decelerationRate를 주었습니다. 세 속성을 세트로 쓰는데 snapToInterval은 요소가 화면보다 작거나 할때 사용하며 얼만큼의 화면이 넘어갈지 결정합니다. snapToAlignment는 snapping되는 리스트의 관계를 정의해주고 decelerationRate는 사용자가 액션을 취한 뒤, 얼마나 빨리 스크롤링이 감속되는지 결정합니다.

function Child({ item, index }) {
  return (
    <Container index={index}>
      <Image resizeMode="stretch" source={item.img} />
      <ContextWrapper>
        <ContextLabel>{item.label}</ContextLabel>
      </ContextWrapper>
    </Container>
  )
}

const Container = styled.View`
  width: ${Dimensions.get('window').width * 0.7}px;
  margin-right: 15px;
`

const Image = styled.Image`
  width: 100%;
  height: 300px;
`

const ContextWrapper = styled.View`
  background-color: rgb(35, 35, 35);
  padding: 10px;
  height: 70px;
`

const ContextLabel = styled.Text`
  font-size: 16px;
  color: white;
`

Child 컴포넌트 요소들도 Container로 레이아웃을 잡아주며 마진값도 잡아줍니다.

스크롤 포지션 잡기

RN에서 스크롤 포지션을 체크할때 몇가지 방법이 있는것 같았는데, 컴포넌트에서 자체적으로 꽤나 도움을 줍니다.

  1. ScrollView에 걸기
function handleScroll (event) {
 console.log(event.nativeEvent.contentOffset.y);
}
...
<ScrollView
onScroll={handleScroll}
/>

위의 handleScroll 함수를 보면 이벤트 객체에 스크롤 y값을 체크할 수 있습니다. 여기서 주의점은 throttle값도 속성으로 주어야 합니다. 그렇지 않으면 you specify… throttle값을 주라는 에러가 납니다.

  1. View
const onLayout = ({
   nativeEvent: {
     layout: { x, y, width, height },
   },
 }) => {

   console.log(y);
 };
 ...
<View
onLayout={onLayOut}
/>

onLayout에 들어가는 함수도 이벤트 객체의 인자를 쭉 타고들어가서 그 값을 받고 추적을 합니다. 두 이벤트의 사용점이 조금 다를것이기에 그때그때 상황에 맞게 알맞게 방법을 택해서 사용하는것이 좋을 것 같습니다.


Written by@jaeyoung-son
배운것을 기록하는 공간입니다.

GitHub