본문 바로가기
코딩생활/ReactNative

[React Native] 3-11 검색 기능 구현하기 (2)

by InfoJun 2023. 3. 30.
반응형

이제 SearchHeader 컴포넌트 UI를 구성하겠습니다.

 

components/SearchHeader.js

import React from 'react';
import {
  View,
  StyleSheet,
  Text,
  useWindowDimensions,
  Pressable,
  TextInput,
} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';

function SearchHeader() {
  const {width} = useWindowDimensions();
  return (
    <View style={[styles.block, {width: width - 32}]}>
      <TextInput
        style={styles.input}
        placeholder={'검색어를 입력하세요'}
        autoFocus
      />
      <Pressable
        style={({pressed}) => [styles.button, pressed && {opacity: 0.5}]}>
        <Icon name={'cancel'} size={20} color={'#9e9e9e'} />
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  block: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  input: {
    flex: 1,
  },
  button: {
    marginLeft: 8,
  },
});

export default SearchHeader;

 

TextInput에 autoFocus를 사용하여 

 

이 컴포넌트가 화면에 나타날 때 자동으로 포커스가 잡힙니다.

 

이제 검색어에 대한 상태를 관리해야합니다.

 

SearchHeade에서 입력한 검색으롤 SearchScreen에서 사용할 수 있어야합니다.

 

현재  SearchHeader 컴포넌트는 MainTab에서 사용하기 때문에 Context를 사용해야합니다.

 

contexts/SearchContext.js

import React, {createContext, useState} from 'react';

const SearchContext = createContext();

export function SearchContextProvider({children}) {
  const [keyword, onChangeText] = useState('');
  return (
    <SearchContext.Provider value={{keyword, onChangeText}}>
      {children}
    </SearchContext.Provider>
  );
}

export default SearchContext;

 

이번에는 별도로 만들 함수가 없습니다.

 

useState로 만든 상태값인 keyword와 업데이트 함수인 onChangeText를

 

SearchContext.Provider의 valuefh 설정하면 됩니다.

 

context를 다 만든 후 App 컴포넌트를 열어 사용합니다.

 

SearchContext와 기존에 만든 LogContext는 의존 관계가 아니기 때문에

 

Provider 컴포넌트의 사용 순서는 중요하지 않습니다

 

App.js

import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import RootStack from './screens/RootStack';
import {LogContextProvider} from './contexts/LogContext';
import {SearchContextProvider} from './contexts/SearchContext';

function App() {
  return (
    <NavigationContainer>
      <SearchContextProvider>
        <LogContextProvider>
          <RootStack />
        </LogContextProvider>
      </SearchContextProvider>
    </NavigationContainer>
  );
}

export default App;

 

SearchHeader 에는 TextInput에 value와 onChangeText Props를 설정합니다.

 

그리고 우측에 Pressable을 눌렀을 때 텍스트가 초기화되도록 설정하겠습니다.

 

conponents/SearchHeader.js

import React, { useContext } from "react";
import {
  View,
  StyleSheet,
  Text,
  useWindowDimensions,
  Pressable,
  TextInput,
} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';//추가
import SearchContext from "../contexts/SearchContext";

function SearchHeader() {
  const {width} = useWindowDimensions();
  //추가
  const { keyword, onChangeText } = useContext(SearchContext);
  // ---
  return (
    <View style={[styles.block, {width: width - 32}]}>
      <TextInput
        style={styles.input}
        placeholder={'검색어를 입력하세요'}
        autoFocus
        //추가
        value={keyword}
        onChangeText={onChangeText}
        // -----
      />
      <Pressable
        style={({pressed}) => [styles.button, pressed && {opacity: 0.5}]}
        onPress={()=> onChangeText('')} //추가
      >
(...)

 

SearchScreen에는 임시로 Text를 사용해 검색어를 화면에 보여주도록 설정합시다.

 

SearchContex가 잘 작동하고 있는지 검증하기 위해서 입니다.

 

screens/SearchScreen.js

import React, {useContext} from 'react';
import {View, StyleSheet, Text} from 'react-native';
import SearchContext from '../contexts/SearchContext';

function SearchScreen({navigation}) {
  const {keyword} = useContext(SearchContext);
  return (
    <View style={styles.block}>
      <Text>{keyword}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  block: {},
});

export default SearchScreen;

 

이제 SearchKeyword에 검색어가 나타 날 것입니다.


검색어 필터링 후 FeedList 재사용하기

 

이제 검색기능을 구현해보겠습니다.

 

배열의 내장 함수 filter 함수를 통해 구현하면 됩니다.

 

screens/SearchSecreen.js

import React, {useContext} from 'react';
import {View, StyleSheet, Text} from 'react-native';
import SearchContext from '../contexts/SearchContext';
import LogContext from '../contexts/LogContext';
import FeedList from "../components/FeedList";

function SearchScreen({navigation}) {
  const {keyword} = useContext(SearchContext);
  //추가
  const {logs} = useContext(LogContext);
  const filtered =
    keyword === ''
      ? []
      : logs.filter(log =>
          [log.title, log.body].some(text => text.includes(keyword)),
        );
  //-------
  return (
    <View style={styles.block}>
      <FeedList logs={filtered} /> //수정
    </View>
  );
}

const styles = StyleSheet.create({
  block: {
    flex : 1 //추가
  },
});

export default SearchScreen;

 

text.includes라는 문자열 내장 함수를 사용했습니다.

 

이는 텍스트에 특정 문자열이 존재하는지 확인하는 함수입니다. 

 

만약 존재한다면 true를 반환하고 그렇지 않으면 false를 반환합니다.

 

[log.title, log.body] 배열에서 사용한 some라는 배열 내장함수는

 

배열 원소 중 특정 조건이 true인 원소가 하나로도 있으면 true를 반환하고

 

모든 원소가 특정 조건을 만족하지 않을시 false를 반환합니다.

 

이제 검색 결과가 나오는지 확인해보겠습니다.


EmptySearchResult 만들기

 

이제 검색기능은 다 구현했고 검색결과가 없을 때 안내문구를 보여주는 컴포넌트를 만들겠습니다.

 

검색결과가 없는 상황은 두가지로

 

첫째는 사용자가 검색어를 입력하지 않았을 때이고,

 

두번째는 검색어와 일치하는 결과물이 없을 때 입니다.

 

components/EmptySearchResult.js

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

const messages = {
  NOT_FOUND: '검색 결과가 없습니다.',
  EMPTY_KEYWORD: '검색어를 입력하세요.',
};

function EmptySearchReault({type}) {
  return (
    <View style={styles.block}>
      <Text style={styles.text}>{messages[type]}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  block: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  text: {
    color: '#9e9e9e',
    fontSize: 16,
  },
});

export default EmptySearchReault;

 

이 컴포넌트는 type라는 Props를 받아옵니다.

 

이제 이 컴포넌트를 SearchScreen에 사용하겠습니다.

 

screens/SearchScreen.js

import React, {useContext} from 'react';
import {View, StyleSheet, Text} from 'react-native';
import SearchContext from '../contexts/SearchContext';
import LogContext from '../contexts/LogContext';
import FeedList from '../components/FeedList';
import EmptySearchReault from "../components/EmptySearchResult";

function SearchScreen({navigation}) {
  const {keyword} = useContext(SearchContext);
  const {logs} = useContext(LogContext);
  const filtered =
    keyword === ''
      ? []
      : logs.filter(log =>
          [log.title, log.body].some(text => text.includes(keyword)),
        );
  //추가
  if (keyword ===''){
    return <EmptySearchReault type={'EMPTY_KEYWORD'} />
  }
  if (filtered.length === 0){
    return  <EmptySearchReault type={'NOT_FOUND'} />
  }
  //---
  return (
    <View style={styles.block}>
      <FeedList logs={filtered} />
    </View>
  );
}

const styles = StyleSheet.create({
  block: {
    flex: 1,
  },
});

export default SearchScreen;
반응형

댓글