Chat App - Part 2 (2)

Sang heon lee·2022년 7월 5일
0

1. 채널 생성 화면

// src/firebase.tsx

import { getFirestore, collection, doc, setDoc } from 'firebase/firestore';

const db = getFirestore(app);

export const createChannel = async ({
  title,
  desc,
}: {
  title: string;
  desc: string;
}) => {
  const channelCollection = collection(db, 'channels');
  const newChanneRef = doc(channelCollection);
  const id = newChanneRef.id;
  const newChannel = { id, title, description: desc, createdAt: Date.now() };

  await setDoc(newChanneRef, newChannel);
  return id;
};
  • 채널 생성화면
import React, { useState, useRef, useEffect, useContext } from 'react';
import styled from 'styled-components/native';
import { Button, Input, ErrorMessage } from '../components';
import { themeType } from '../theme';
import { StackNavigationProp } from '@react-navigation/stack';
import { MainStackParamList } from '../navigations/Main';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import { ProgressContext } from '../contexts';
import { createChannel } from '../firebase';
import { Alert } from 'react-native';

interface ThemeProps {
  theme: themeType;
}

const Container = styled.View<ThemeProps>`
  flex: 1;
  background-color: ${({ theme }) => theme.background};
  justify-content: center;
  align-items: center;
  padding: 0 20px;
`;

const StyledText = styled.Text`
  font-size: 30px;
`;

type ChannelCreationScreenNavPropsType = StackNavigationProp<
  MainStackParamList,
  'ChannelCreation'
>;

type Props = {
  navigation: ChannelCreationScreenNavPropsType;
};

const ChannelCreation = ({ navigation }: Props) => {
  const [title, setTitle] = useState('');
  const [desc, setDesc] = useState('');
  const [errorMessage, setErrorMessage] = useState('');
  const [disabled, setDisabled] = useState(true);

  const refDesc = useRef(null);

  const { spinner } = useContext(ProgressContext);

  useEffect(() => {
    setDisabled(!(title && !errorMessage));
  }, [title, errorMessage]);

  const _handleTitleChange = (title: string) => {
    setTitle(title);
    setErrorMessage(title.trim() ? '' : 'Please enter the title');
  };

  const _handleDescChange = (desc: string) => {
    setDesc(desc);
    setErrorMessage(title.trim() ? '' : 'Please enter the Description');
  };

  const _handleCreateBtnPress = async () => {
    try {
      spinner.start();
      const id = await createChannel({
        title: title.trim(),
        desc: desc.trim(),
      });
      navigation.replace('Channel', { id, title });
    } catch (e) {
      Alert.alert('Creation Error', e.message);
    } finally {
      spinner.stop();
    }
  };

  return (
    <KeyboardAwareScrollView
      contentContainerStyle={{ flex: 1 }}
      extraScrollHeight={20}
      enableOnAndroid={true}
    >
      <Container>
        <StyledText>Channel Creation</StyledText>
        <Input
          label="Title"
          value={title}
          onChangeText={_handleTitleChange}
          onSubmitEditing={() => refDesc.current.focus()}
          onBlur={() => setTitle(title.trim())}
          placeholder="Title"
          returnKeyType="next"
          maxLength={20}
        />
        <Input
          ref={refDesc}
          label="Description"
          value={desc}
          onChangeText={_handleDescChange}
          onSubmitEditing={_handleCreateBtnPress}
          onBlur={() => setDesc(desc.trim())}
          placeholder="Description"
          returnKeyType="done"
          maxLength={40}
        />
        <ErrorMessage message={errorMessage} />
        <Button
          title="Create"
          disabled={disabled}
          // onPress={() => navigation.replace('Channel')}
          onPress={_handleCreateBtnPress}
        />
      </Container>
    </KeyboardAwareScrollView>
  );
};

export default ChannelCreation;

2. 채널 목록 화면 - FlatList 컴포넌트

  • FlatList 를 활용하여 스크롤 되는 화면을 만든다.

  • ScrollView 컴포넌트와 다른점은 필요한 데이터만 렌더링 하고 나머지는 추후에 렌더링한다.

  • https://reactnative.dev/docs/flatlist

  • windowSize : https://reactnative.dev/docs/optimizing-flatlist-configuration
    현재화면외에 미리 렌더링할 화면 갯수를 지정
    (Ex: 초기화면에 5개가 렌더링 되어 있고 window size가 10개 라면
    5(한화면에 렌더링 갯수) * {0(초기화면이라) + 1(현재 화면)+ 10(windowSize)) = 50개 : 초기에 50개가 렌더링 되고 스크롤 내릴수록 렌더링 갯수가 늘어난다.)

  • 변화없는 컴포넌트는 재렌더링을 방지하기 위하여 React.memo()를 이용한다.

// src/screens/ChannelList.tsx

const channels = [];
for (let i = 0; i < 1000; i++) {
  channels.push({
    id: i,
    title: `title: ${i}`,
    description: `desc: ${i}`,
    createdAt: i,
  });
}

const ItemContainer = styled.TouchableOpacity<ThemeProps>`
  flex-direction: row;
  align-items: center;
  border-bottom-width: 1px;
  border-color: ${({ theme }) => theme.itemBorder};
  padding: 15px 20px;
`;

const ItemTextContainer = styled.View`
  flex: 1;
  flex-direction: column;
`;

const ItemTitle = styled.Text<ThemeProps>`
  font-size: 20px;
  font-weight: 600;
  color: ${({ theme }) => theme.text};
`;

const ItemDesc = styled.Text<ThemeProps>`
  font-size: 16px;
  margin-top: 5px;
  color: ${({ theme }) => theme.itemDesc};
`;

const ItemTime = styled.Text<ThemeProps>`
  font-size: 12px;
  color: ${({ theme }) => theme.itemTime};
`;

const ItemIcon = styled(MaterialIcons).attrs(({ theme }) => {
  return {
    name: 'keyboard-arrow-right',
    size: 24,
    color: theme.itemIcon,
  };
})``;

interface itemProps {
  item: {
    id: string;
    title: string;
    description: string;
    createdAt: number;
  };
  onPress: () => void;
}

const Item = React.memo(
  ({ item: { id, title, description, createdAt }, onPress }: itemProps) => {
    console.log(id);

    return (
      <ItemContainer>
        <ItemTextContainer>
          <ItemTitle>{title}</ItemTitle>
          <ItemDesc>{description}</ItemDesc>
        </ItemTextContainer>
        <ItemTime>{createdAt}</ItemTime>
        <ItemIcon />
      </ItemContainer>
    );
  },
);

const ChannelList = ({ navigation }: Props) => {
  return (
    <Container>
      <StyledText>Channel List</StyledText>
      <FlatList
        data={channels}
        renderItem={({ item }) => <Item item={item} onPress={() => {}} />}
        keyExtractor={item => item['id'].toString()}
        windowSize={5}
      />
    </Container>
  );
};

export default ChannelList;
profile
개초보

0개의 댓글