Gihak111 Navbar

컴포넌트와 리액트 훅

리액트 네이티브에서 컴포넌트와 훅의 개념은 앱의 구조와 상태 관리를 효과적으로 다루기 위해 필수적이다. 여기서는 리액트 네이티브의 컴포넌트와 훅을 깊이 있게 다루며, 각 훅의 기능과 사용법을 상세히 설명하겠다. 또한, 다양한 훅을 실용적인 예제와 함께 설명하겠다.

1. 컴포넌트

리액트 네이티브의 컴포넌트는 UI를 구성하는 기본 단위이다. 컴포넌트는 함수형 컴포넌트와 클래스형 컴포넌트로 나눌 수 있다.

1.1 함수형 컴포넌트

함수형 컴포넌트는 간단한 자바스크립트 함수로 정의되며, 주로 렌더링 로직을 처리한다. 함수형 컴포넌트는 훅을 사용하여 상태를 관리하고, 사이드 이펙트를 처리할 수 있다.

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

// 함수형 컴포넌트 정의
const Greeting = ({ name }) => {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Hello, {name}!</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정한다
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  text: {
    fontSize: 20,    // 텍스트 크기를 20픽셀로 설정한다
    color: 'blue',   // 텍스트 색상을 파란색으로 설정한다
  },
});

export default Greeting;

1.2 클래스형 컴포넌트

클래스형 컴포넌트는 자바스크립트 클래스를 사용하여 정의되며, 상태와 생명주기 메서드를 가질 수 있다. 복잡한 상태 로직과 생명주기 관리가 필요한 경우에 적합하다.

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

// 클래스형 컴포넌트 정의
class Greeting extends Component {
  render() {
    const { name } = this.props;
    return (
      <View style={styles.container}>
        <Text style={styles.text}>Hello, {name}!</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정한다
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  text: {
    fontSize: 20,    // 텍스트 크기를 20픽셀로 설정한다
    color: 'blue',   // 텍스트 색상을 파란색으로 설정한다
  },
});

export default Greeting;

2. 리액트 훅

리액트 훅은 함수형 컴포넌트에서 상태와 생명주기 기능을 추가할 수 있게 해주는 함수들이다. 여러 훅이 있으며, 각 훅은 특정한 목적을 가진다. 주요 훅과 그 사용법에 대해 자세히 설명하겠다.

2.1 useState

useState 훅은 함수형 컴포넌트에서 상태를 추가할 수 있게 해준다. 이 훅은 현재 상태와 상태를 업데이트할 수 있는 함수를 반환한다.

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

// useState 훅을 사용하는 함수형 컴포넌트
const Counter = () => {
  const [count, setCount] = useState(0); // 상태 변수와 업데이트 함수 선언

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Count: {count}</Text>
      <Button
        title="Increment"
        onPress={() => setCount(count + 1)} // 버튼 클릭 시 상태 업데이트
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  text: {
    fontSize: 20,    // 텍스트 크기를 20픽셀로 설정
    color: 'black',  // 텍스트 색상을 검정색으로 설정
  },
});

export default Counter;

2.2 useEffect

useEffect 훅은 부수 효과(side effects)를 처리하는 데 사용된다. 컴포넌트가 렌더링될 때 특정 작업을 수행하거나 컴포넌트가 언마운트될 때 정리 작업을 수행할 수 있다.

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

// useEffect 훅을 사용하는 함수형 컴포넌트
const Timer = () => {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setSeconds(prevSeconds => prevSeconds + 1); // 매초 상태 업데이트
    }, 1000);

    return () => clearInterval(intervalId); // 언마운트 시 인터벌 정리
  }, []); // 빈 배열을 전달하면 컴포넌트가 마운트될 때만 실행

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Time: {seconds}s</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  text: {
    fontSize: 20,    // 텍스트 크기를 20픽셀로 설정
    color: 'black',  // 텍스트 색상을 검정색으로 설정
  },
});

export default Timer;

2.3 useContext

useContext 훅은 React의 Context API를 사용하여 전역 상태를 함수형 컴포넌트에서 쉽게 접근할 수 있게 해준다. Context를 생성하고, 제공하고, 소비하는 과정을 간단히 할 수 있다.

import React, { createContext, useContext, useState } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

// Context 생성
const ThemeContext = createContext();

// 컨텍스트 공급자 컴포넌트
const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value=>
      {children}
    </ThemeContext.Provider>
  );
};

// 컨텍스트를 사용하는 컴포넌트
const ThemedComponent = () => {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <View style={[styles.container, { backgroundColor: theme === 'light' ? 'white' : 'black' }]}>
      <Text style=>Current Theme: {theme}</Text>
      <Button title="Toggle Theme" onPress={toggleTheme} />
    </View>
  );
};

const App = () => {
  return (
    <ThemeProvider>
      <ThemedComponent />
    </ThemeProvider>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
});

export default App;

2.4 useReducer

useReducer 훅은 상태 로직이 복잡할 때 유용하다. 상태와 상태를 업데이트하는 리듀서를 관리하는 함수의 조합으로 상태를 업데이트할 수 있다.

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

// 리듀서 함수 정의
const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

// useReducer

 훅을 사용하는 함수형 컴포넌트
const Counter = () => {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Count: {state.count}</Text>
      <Button title="Increment" onPress={() => dispatch({ type: 'INCREMENT' })} />
      <Button title="Decrement" onPress={() => dispatch({ type: 'DECREMENT' })} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  text: {
    fontSize: 20,    // 텍스트 크기를 20픽셀로 설정
    color: 'black',  // 텍스트 색상을 검정색으로 설정
  },
});

export default Counter;

2.5 useCallback

useCallback 훅은 콜백 함수를 메모이제이션하여 성능을 최적화하는 데 사용된다. 특정 값이 변경되지 않는 한 동일한 콜백 함수를 재사용할 수 있다.

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

// ExpensiveComponent 컴포넌트 정의
const ExpensiveComponent = React.memo(({ onClick }) => {
  console.log('ExpensiveComponent re-rendered');
  return <Button title="Click me" onPress={onClick} />;
});

const App = () => {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Count: {count}</Text>
      <ExpensiveComponent onClick={handleClick} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  text: {
    fontSize: 20,    // 텍스트 크기를 20픽셀로 설정
    color: 'black',  // 텍스트 색상을 검정색으로 설정
  },
});

export default App;

2.6 useMemo

useMemo 훅은 계산 비용이 높은 값의 메모이제이션을 제공하여 성능 최적화를 돕는다. 특정 값이 변경되지 않는 한 동일한 계산 결과를 재사용한다.

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

// ExpensiveComponent 컴포넌트 정의
const ExpensiveComponent = ({ value }) => {
  const computedValue = useMemo(() => {
    let sum = 0;
    for (let i = 0; i < 1000000; i++) {
      sum += i;
    }
    return sum;
  }, [value]);

  return <Text style={styles.text}>{`Computed Value: ${computedValue}`}</Text>;
};

const App = () => {
  const [value, setValue] = useState(0);

  return (
    <View style={styles.container}>
      <ExpensiveComponent value={value} />
      <Button title="Change Value" onPress={() => setValue(value + 1)} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  text: {
    fontSize: 20,    // 텍스트 크기를 20픽셀로 설정
    color: 'black',  // 텍스트 색상을 검정색으로 설정
  },
});

export default App;

2.7 useRef

useRef 훅은 컴포넌트의 렌더링 사이에 값을 유지할 수 있게 해준다. 주로 DOM 요소 에 대한 참조를 저장하거나, 렌더링 간에 유지해야 할 값(예: 타이머 ID)을 저장하는 데 사용된다.

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

// useRef 훅을 사용하는 함수형 컴포넌트
const Timer = () => {
  const countRef = useRef(0); // countRef를 선언하고 초기값 0을 설정

  useEffect(() => {
    const intervalId = setInterval(() => {
      countRef.current += 1; // countRef.current 값을 증가시킴
      console.log(`Timer: ${countRef.current}s`);
    }, 1000);

    return () => clearInterval(intervalId); // 컴포넌트 언마운트 시 인터벌 정리
  }, []);

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Check the console for timer updates</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  text: {
    fontSize: 20,    // 텍스트 크기를 20픽셀로 설정
    color: 'black',  // 텍스트 색상을 검정색으로 설정
  },
});

export default Timer;

2.8 useImperativeHandle

useImperativeHandle 훅은 부모 컴포넌트가 자식 컴포넌트의 인스턴스 메서드와 속성에 접근할 수 있도록 해준다. 주로 forwardRef와 함께 사용된다.

import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

// 자식 컴포넌트 정의
const Child = forwardRef((props, ref) => {
  const [value, setValue] = useState(0);

  useImperativeHandle(ref, () => ({
    increment() {
      setValue(prevValue => prevValue + 1); // 외부에서 호출 시 값 증가
    },
    getValue() {
      return value; // 외부에서 호출 시 현재 값 반환
    }
  }));

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Value: {value}</Text>
    </View>
  );
});

// 부모 컴포넌트 정의
const Parent = () => {
  const childRef = useRef();

  return (
    <View style={styles.container}>
      <Child ref={childRef} />
      <Button
        title="Increment Child Value"
        onPress={() => childRef.current.increment()} // 버튼 클릭 시 자식 컴포넌트 메서드 호출
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  text: {
    fontSize: 20,    // 텍스트 크기를 20픽셀로 설정
    color: 'black',  // 텍스트 색상을 검정색으로 설정
  },
});

export default Parent;

종합 예제

아래 예제는 컴포넌트와 훅을 종합적으로 사용하여 상태 관리, 부수 효과, 성능 최적화, 컨텍스트 및 참조 관리를 어떻게 하는지 보여준다.

import React, { useState, useEffect, useContext, useReducer, useCallback, useMemo, useRef, createContext, forwardRef, useImperativeHandle } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

// Context 생성
const ThemeContext = createContext();

// 리듀서 함수 정의
const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

// 컨텍스트 공급자 컴포넌트
const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value=>
      {children}
    </ThemeContext.Provider>
  );
};

// 자식 컴포넌트 정의
const Child = forwardRef((props, ref) => {
  const [value, setValue] = useState(0);

  useImperativeHandle(ref, () => ({
    increment() {
      setValue(prevValue => prevValue + 1);
    },
    getValue() {
      return value;
    }
  }));

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Child Value: {value}</Text>
    </View>
  );
});

// ExpensiveComponent 컴포넌트 정의
const ExpensiveComponent = React.memo(({ value }) => {
  const computedValue = useMemo(() => {
    let sum = 0;
    for (let i = 0; i < 1000000; i++) {
      sum += i;
    }
    return sum;
  }, [value]);

  return <Text style={styles.text}>{`Computed Value: ${computedValue}`}</Text>;
});

// 메인 컴포넌트 정의
const App = () => {
  const [state, dispatch] = useReducer(reducer, { count: 0 });
  const [value, setValue] = useState(0);
  const childRef = useRef();

  useEffect(() => {
    const intervalId = setInterval(() => {
      console.log('Interval running');
    }, 1000);

    return () => clearInterval(intervalId);
  }, []);

  const handleClick = useCallback(() => {
    setValue(value + 1);
  }, [value]);

  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <ThemeProvider>
      <View style={[styles.container, { backgroundColor: theme === 'light' ? 'white' : 'black' }]}>
        <Text style=>Current Theme: {theme}</Text>
        <Button title="Toggle Theme" onPress={toggleTheme} />
        <Text style={styles.text}>Count: {state.count}</Text>
        <Button title="Increment Count" onPress={() => dispatch({ type: 'INCREMENT' })} />
        <Button title="Decrement Count" onPress={() => dispatch({ type: 'DECREMENT' })} />
        <ExpensiveComponent value={value} />
        <Button title="Change Value" onPress={handleClick} />
        <Child ref={childRef} />
        <Button title="Increment Child Value" onPress={() => childRef.current.increment()} />
      </View>
    </ThemeProvider>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  text: {
    fontSize: 20,    // 텍스트 크기를 20픽셀로 설정
    color: 'black',  // 텍스트 색상을 검정색으로 설정
  },
});

export default App;

이 예제는 다양한 훅을 사용하여 리액트 네이티브 애플리케이션에서 상태 관리, 성능 최적화, 부수 효과 처리, 컨텍스트와 참조 관리를 어떻게 하는지를 보여준다. 각 훅의 기능을 활용하여 효율적이고 유지보수하기 쉬운 코드를 작성할 수 있다.

Fetch API와 커스텀 훅

리액트 네이티브에서 Fetch API커스텀 훅은 데이터를 비동기적으로 가져오고 재사용 가능한 로직을 관리하는 데 중요한 역할을 한다. 이 두 가지를 깊이 있게 다루며, 각 개념과 그 사용법을 자세히 설명하겠다.

1. Fetch API

Fetch API는 웹에서 자원을 네트워크를 통해 가져오는 방법을 제공하는 자바스크립트 API이다. 리액트 네이티브에서도 데이터를 비동기적으로 가져올 때 널리 사용된다.

1.1 기본 사용법

Fetch API를 사용하여 HTTP 요청을 보내고 응답을 처리하는 기본적인 방법이다.

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

// 데이터 Fetching을 위한 함수형 컴포넌트
const DataFetcher = () => {
  const [data, setData] = useState(null); // 데이터 상태
  const [loading, setLoading] = useState(true); // 로딩 상태
  const [error, setError] = useState(null); // 에러 상태

  useEffect(() => {
    // 비동기 데이터 fetching 함수
    const fetchData = async () => {
      try {
        const response = await fetch('https://api.example.com/data'); // API 호출
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        const result = await response.json(); // JSON 형식으로 응답 데이터 파싱
        setData(result); // 상태 업데이트
      } catch (error) {
        setError(error); // 에러 상태 업데이트
      } finally {
        setLoading(false); // 로딩 상태 종료
      }
    };

    fetchData(); // 데이터 fetching 호출
  }, []); // 빈 배열을 전달하여 컴포넌트 마운트 시 한 번만 호출

  if (loading) {
    return <ActivityIndicator size="large" color="#0000ff" />; // 로딩 중 표시
  }

  if (error) {
    return <Text style={styles.error}>Error: {error.message}</Text>; // 에러 메시지 표시
  }

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Data: {JSON.stringify(data)}</Text> {/* 데이터를 문자열로 변환하여 표시 */}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  text: {
    fontSize: 18,    // 텍스트 크기를 18픽셀로 설정
    color: 'black',  // 텍스트 색상을 검정색으로 설정
  },
  error: {
    color: 'red',    // 에러 텍스트 색상을 빨간색으로 설정
  },
});

export default DataFetcher;

1.2 POST 요청

POST 요청을 사용하여 데이터를 서버에 보내는 방법이다.

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

const PostData = () => {
  const [text, setText] = useState('');
  const [response, setResponse] = useState(null);
  const [error, setError] = useState(null);

  const handleSubmit = async () => {
    try {
      const response = await fetch('https://api.example.com/submit', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ text }), // 서버로 전송할 데이터
      });

      if (!response.ok) {
        throw new Error('Network response was not ok');
      }

      const result = await response.json(); // JSON 형식으로 응답 데이터 파싱
      setResponse(result); // 상태 업데이트
    } catch (error) {
      setError(error); // 에러 상태 업데이트
    }
  };

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.input}
        placeholder="Enter some text"
        value={text}
        onChangeText={setText}
      />
      <Button title="Submit" onPress={handleSubmit} />
      {response && <Text style={styles.text}>Response: {JSON.stringify(response)}</Text>}
      {error && <Text style={styles.error}>Error: {error.message}</Text>}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  input: {
    height: 40,
    borderColor: 'gray',
    borderWidth: 1,
    width: '80%',
    marginBottom: 10,
    paddingHorizontal: 8,
  },
  text: {
    fontSize: 18,    // 텍스트 크기를 18픽셀로 설정
    color: 'black',  // 텍스트 색상을 검정색으로 설정
  },
  error: {
    color: 'red',    // 에러 텍스트 색상을 빨간색으로 설정
  },
});

export default PostData;

2. 커스텀 훅

커스텀 훅은 리액트의 훅을 조합하여 재사용 가능한 로직을 만드는 방법이다. 훅을 재사용하고 컴포넌트 간의 상태나 로직을 공유할 때 유용하다.

2.1 기본 사용법

커스텀 훅은 기존 훅을 조합하여 새로운 훅을 정의하는 방법이다. 예를 들어, API 호출과 관련된 로직을 커스텀 훅으로 추출할 수 있다.

import { useState, useEffect } from 'react';

// 커스텀 훅 정의
const useFetch = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        const result = await response.json();
        setData(result);
      } catch (error) {
        setError(error);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};

export default useFetch;

2.2 커스텀 훅 사용 예제

위에서 정의한 커스텀 훅을 사용하는 방법이다.

import React from 'react';
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
import useFetch from './useFetch'; // 커스텀 훅을 임포트

const DataFetcher = () => {
  const { data, loading, error } = useFetch('https://api.example.com/data'); // 커스텀 훅 사용

  if (loading) {
    return <ActivityIndicator size="large" color="#0000ff" />;
  }

  if (error) {
    return <Text style={styles.error}>Error: {error.message}</Text>;
  }

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Data: {JSON.stringify(data)}</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  text: {
    fontSize: 18,    // 텍스트 크기를 18픽셀로 설정
    color: 'black',  // 텍스트 색상을 검정색으로 설정
  },
  error: {
    color: 'red',    // 에러 텍스트 색상을 빨간색으로 설정
  },
});

export default DataFetcher;

2.3 훅 조합하기

여러 커스텀 훅을 조합하여 더 복잡한 로직을 처리할 수 있다. 예를 들어, 입력 필드와 API 요청을 처리하는 훅을 조합할 수 있다.

import { useState } from 'react';

// 입력 필드의 값을 관리하는 커스텀 훅
const useInput = (initialValue) => {
  const [value, setValue] = useState(initialValue);

  const handleChange = (newValue) => {
    setValue(newValue);
  };

  return [value, handleChange];
};

export default useInput;
import React from 'react';
import { View, TextInput, Button, Text, StyleSheet } from 'react-native';
import useFetch from './useFetch';
import useInput from './

useInput';

const PostData = () => {
  const [text, setText] = useInput('');
  const [response, setResponse] = useState(null);
  const [error, setError] = useState(null);

  const handleSubmit = async () => {
    try {
      const response = await fetch('https://api.example.com/submit', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ text }),
      });

      if (!response.ok) {
        throw new Error('Network response was not ok');
      }

      const result = await response.json();
      setResponse(result);
    } catch (error) {
      setError(error);
    }
  };

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.input}
        placeholder="Enter some text"
        value={text}
        onChangeText={setText}
      />
      <Button title="Submit" onPress={handleSubmit} />
      {response && <Text style={styles.text}>Response: {JSON.stringify(response)}</Text>}
      {error && <Text style={styles.error}>Error: {error.message}</Text>}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  input: {
    height: 40,
    borderColor: 'gray',
    borderWidth: 1,
    width: '80%',
    marginBottom: 10,
    paddingHorizontal: 8,
  },
  text: {
    fontSize: 18,    // 텍스트 크기를 18픽셀로 설정
    color: 'black',  // 텍스트 색상을 검정색으로 설정
  },
  error: {
    color: 'red',    // 에러 텍스트 색상을 빨간색으로 설정
  },
});

export default PostData;

종합 예제

import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet, ActivityIndicator } from 'react-native';
import useFetch from './useFetch';
import useInput from './useInput';

const DataForm = () => {
  const [text, setText] = useInput('');
  const { data, loading, error } = useFetch('https://api.example.com/data');
  const [response, setResponse] = useState(null);

  const handleSubmit = async () => {
    try {
      const response = await fetch('https://api.example.com/submit', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ text }),
      });

      if (!response.ok) {
        throw new Error('Network response was not ok');
      }

      const result = await response.json();
      setResponse(result);
    } catch (error) {
      setResponse(null);
      setError(error);
    }
  };

  return (
    <View style={styles.container}>
      {loading && <ActivityIndicator size="large" color="#0000ff" />}
      {error && <Text style={styles.error}>Error: {error.message}</Text>}
      {data && <Text style={styles.text}>Data: {JSON.stringify(data)}</Text>}
      <TextInput
        style={styles.input}
        placeholder="Enter some text"
        value={text}
        onChangeText={setText}
      />
      <Button title="Submit" onPress={handleSubmit} />
      {response && <Text style={styles.text}>Response: {JSON.stringify(response)}</Text>}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,          // 부모 컨테이너의 모든 공간을 차지하도록 설정
    justifyContent: 'center', // 세로 방향으로 중앙 배치
    alignItems: 'center',     // 가로 방향으로 중앙 배치
  },
  input: {
    height: 40,
    borderColor: 'gray',
    borderWidth: 1,
    width: '80%',
    marginBottom: 10,
    paddingHorizontal: 8,
  },
  text: {
    fontSize: 18,    // 텍스트 크기를 18픽셀로 설정
    color: 'black',  // 텍스트 색상을 검정색으로 설정
  },
  error: {
    color: 'red',    // 에러 텍스트 색상을 빨간색으로 설정
  },
});

export default DataForm;