본문 바로가기

Frontend/React-Native

[React-Native]Touch event, Button

728x90

 

 

 

개발환경 시작하기

이제 개발환경 시작하기는 생략하도록 하겠습니다

react_native_01이라는 새로운 프로젝트를 생성하여 진행하였습니다.

react-native init react_native_02

에뮬레이터까지 틀어주도록 합시다

 

 

 

Setup : Component 분리

외부파일에 작성한 것을 import하여 사용하는 방법(현업에서 많이 사용)

별도의 외부파일을 작성하여, App.js에 불러와 하나의 컴포넌트처럼 사용해보자. (코드가 간결해지고 유지보수가 쉬워짐!)

header.js라는 파일을 작성하여, App.js에 불러오는 방식을 사용할 것 

 

src폴더를 만들어서 거기에 header.js라는 파일을 하나 만들어 줍니다.

header.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

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

//참고 : jsx component를 return하므로 () => (이부분이 소괄호)
const Header = () => (
  <View style={styles.header}>
    <Text>this is header</Text>
  </View>
);

const styles = StyleSheet.create({
  header: {
    backgroundColor: 'pink',
    alignItems: 'center',
    padding: 5,
    width: '100%',
  },
});

export default Header;

다음과 같이 작성한 후 Header를 export해준다.

 

 

App.js에 Header로 export된 것을 import한다.

import Header from './src/header';

 

헤더에 작성한 코드는 보여지는 것 이므로 View태그 안에 사용해야하며, 다음과 같이 컴포넌트 태그들과 유사하게 사용해준다

 

App.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

import React, {Component} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import Header from './src/header';

class App extends Component {
  render() {
    return (
      <View style={styles.mainView}>
        <Header />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  mainView: {
    flex: 1,
    backgroundColor: 'white',
    paddingTop: 50,
    alignItems: 'center',
    justifyContent: 'center',
  },
  subView: {
    backgroundColor: 'yellow',
    marginBottom: 10,
  },
  anotherSubView: {
    flex: 2,
    backgroundColor: 'yellow',
    marginBottom: 10,
    width: '100%',
    alignItems: 'center',
    justifyContent: 'center',
  },
  mainText: {
    fontSize: 20,
    fontWeight: 'normal',
    color: 'red',
    padding: 20,
  },
});

export default App;

 

 

참고) 두 함수의 차이

exampleFunciton = () => ( 함수내용 )

* jsx(javascript XML : 자바스크립트 확장 문법)를 return함

 

exampleFunciton = () => { 함수내용 }

return 되는 jsx 컴포넌트가 존재하지 않음

 

*javascript XML : 자바스크립트 확장 문법

문자열 태그도 아니지만, HTML문법도 아니며, 태그에 감싸져 변수에 할당되는 것 

예시)

const example = <tag>hello world</tag>

 

 

 

State를 사용하여 변형

State를 사용하여, header에서 출력해주는 문구를 바꾸어보도록 한다.

App.js에서 state를 만들어 준 후 이를 props를 이용해 header에서 받아 출력한다.

 

App.js

state로 앱의 이름을 할당한 후, Header태그에 변수로 넣어준다. 

class App extends Component {
  state = {
    appName: 'My First App',
  };

  render() {
    return (
      <View style={styles.mainView}>
        <Header name={this.state.appName} />
      </View>
    );
  }
}

 

header.js

매개변수에 props를 넣어준 후, 이를 이용해 출력문구를 변경해준다.

const Header = props => (
  <View style={styles.header}>
    <Text>{props.name}</Text>
  </View>
);

 

 

Header에 this is header라는 문구 대신 앱의 이름이 출력된 것을 확인할 수 있다.

이제 Touch event를 실습해볼 환경구성이 끝났다.

 

 

Touch Event

뷰가 터치에 반응하도록 하는 기능

https://reactnative.dev/docs/touchableopacity

 

TouchableOpacity · React Native

If you're looking for a more extensive and future-proof way to handle touch-based input, check out the Pressable API.

reactnative.dev

 

TouchableOpacity IMPORT
import {TouchableOpacity} from 'react-native';

기능을 사용하기 위해 import를 해줍니다.

 

사용예시)

헤더에 터치시 alert가 뜨는 기능을 추가해보았다

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

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

//참고 : jsx component를 return하므로 () => (이부분이 소괄호)
const Header = props => (
  <TouchableOpacity style={styles.header} onPress={() => alert('hello world')}>
    <View style={styles.header}>
      <Text>{props.name}</Text>
    </View>
  </TouchableOpacity>
);

const styles = StyleSheet.create({
  header: {
    backgroundColor: 'pink',
    alignItems: 'center',
    padding: 5,
    width: '100%',
  },
});

export default Header;

결과)

터치전 터치시

 

 

 

Property

TouchableOpacity가 가진 여러 특성

1. TouchableWithoutFeedbackProps를 상속받음

Touchable Opacity와 같으나 투명해지지(위 화면 오른쪽부분 배경이 약간 투명해진 효과) 않음 

예시)

주의할 점은 TouchableWithoutFeedback은 style이 적용되지 않아 styled을 view에 넣어주어야 한다는 사실

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

const Header = props => (
  <TouchableWithoutFeedback onPress={() => alert('hello world')}>
    <View style={styles.header}>
      <Text>{props.name}</Text>
    </View>
  </TouchableWithoutFeedback>
);

 

근데 나는 사실 차이잘모르겠다..뭐가 투명하다는겨?

 

2. 다양하게 사용할 수 있는 함수

onPress 대신 다른 함수를 사용할 수 있다.

이름 효과
onPress 터치를 하고 손을 떼었을 때 실행됨 
onLongPress 오래 터치하고 있을 때 실행됨
onPressIn 터치를 하자마자 실행됨
onPressOut 터치를 하고 손을 떼었을 때 실행됨(onPress보다 앞서서 실행됨)

 

3. Text나 View에도 이를 적용할 수 있음

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

import React, {Component} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import Header from './src/header';

class App extends Component {
  state = {
    appName: 'My First App',
  };

  render() {
    return (
      <View style={styles.mainView}>
        {/* <Header name={this.state.appName} /> */}
        <Text style={styles.mainText} onPress={() => alert('touch event')}>
          Hello world
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  mainView: {
	//생략 
  mainText: {
    fontSize: 20,
    fontWeight: 'normal',
    color: 'red',
    padding: 20,
  },
});

export default App;

결과)

터치전 터치시

Touchevent를 import하지 않아도 되는 모양(?)...

 

 

Button

말그대로 버튼을 만들어 보자

 

Button생성 

Button IMPORT

먼저, 버튼을 사용하기 위해서는 react-native에서 임포트해와야한다.

import {Button} from 'react-native'

 

Button 생성

주의할 점은 버튼을 생성할 때는 title이 필수로 들어가야한다는 점이다.

Button은 View태그 내부에 넣어주어야 한다.

버튼을 따로 관리해주기 위해 generator.js라는 파일을 src하위에 생성해준 후 다음과 같이 작성한다.

 

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

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

const Generator = () => {
  return (
    <View>
      <Button title={'Add Number'} />
    </View>
  );
};

export default Generator;

 

 

App.js에 generator를 import하여 다음과 같이 추가해준다.

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

import React, {Component} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import Header from './src/header';
import Generator from './src/generator';

class App extends Component {
  state = {
    appName: 'My First App',
  };

  render() {
    return (
      <View style={styles.mainView}>
        <Header name={this.state.appName} />
        <View>
          <Text style={styles.mainText} onPress={() => alert('touch event')}>
            Hello world
          </Text>
        </View>

        <Generator />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  mainView: {
    flex: 1,
    backgroundColor: 'white',
    paddingTop: 50,
    alignItems: 'center',
    justifyContent: 'center',
  },
  subView: {
    backgroundColor: 'yellow',
    marginBottom: 10,
  },
  anotherSubView: {
    flex: 2,
    backgroundColor: 'yellow',
    marginBottom: 10,
    width: '100%',
    alignItems: 'center',
    justifyContent: 'center',
  },
  mainText: {
    fontSize: 20,
    fontWeight: 'normal',
    color: 'red',
    padding: 20,
  },
});

export default App;

 

다음과 같이 일단 아무기능없는 버튼이 하나 생성된 것을 볼 수 있다. 

title 이 Add Number인 버튼이 생성됨

참고> ios와 안드로이드는 default 모양이 다르다

 

Button Style

버튼에 Style을 넣기

Style강의에서 배운대로 styleSheet를 이용한 후 버튼을 감싼 View에 적용한다

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

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

const Generator = () => {
  return (
    <View style={styles.generator}>
      <Button title={'Add Number'} />
    </View>
  );
};

const styles = StyleSheet.create({
  generator: {
    backgroundColor: '#FFDFB0',
    alignItems: 'center',
    padding: 5,
    width: '100%',
  },
});

export default Generator;

 

 

Button에 기능 부여

버튼 태그 내에 onPress함수를 이용하여 버튼에 기능을 추가 할 수 있다.

일단 Alert를 추가해본다.

 

Alert Button
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

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

const Generator = () => {
  return (
    <View style={styles.generator}>
      <Button title={'Add Number'} onPress={() => alert('Button pressed!')} />
    </View>
  );
};

const styles = StyleSheet.create({
  generator: {
    backgroundColor: '#FFDFB0',
    alignItems: 'center',
    padding: 5,
    width: '100%',
  },
});

export default Generator;

 

Add Number를 누를 경우 alert창이 뜸 

 

 

 

 

+ 기능을 가진 버튼을 만들어 보자! 

위에서 배운 것들을 이용하여, 버튼을 누르면 random하게 번호가 생성되어 저장되는 기능을 가진 버튼을 만들어보자

 

1. 버튼에 의해 생성될 데이터 저장소 (State)

버튼에 의해 random하게 생성된 번호는 App.js에서 관리해야 하므로, App.js에서 State에 이를 관리할 배열을 추가해준다.

App.js

class App extends Component {
  state = {
    appName: 'My First App',
    random: [36, 999],
  };
  
  
  //생략
  
  }

다음과 같이 random이라는 state를 추가한다.

 

 

2.  랜덤한 변수를 생성해내는 함수를 생성한다.

일단 자세한 기능을 넣기전 확인용으로 alert를 이용한다.

App.js

onAddRandomNum = () => {
    alert('add random Number!');
  };

 

3. (2)에서 만든 함수를 generator에 넘겨준다
class App extends Component {
  state = {
    appName: 'My First App',
    random: [36, 999],
  };

  onAddRandomNum = () => {
    alert('add random Number!');
  };

  render() {
    return (
      <View style={styles.mainView}>
		//생략

        <Generator add={this.onAddRandomNum} />
      </View>
    );
  }
}

Generator에 변수로 제작한 함수를 넘겨준다 

 

4. props를 이용해 받아온 함수를 Button태그에 넣는다.

generator.js

const Generator = props => {
  return (
    <View style={styles.generator}>
      <Button title={'Add Number'} onPress={() => props.add()} />
    </View>
  );
};


export default Generator;

props를 이용해 전달받은 변수(함수)를 사용한다. 

()이거 안넣어주면 실행안됨 

 

이제 버튼을 누르면 alert가 뜬다.

 

5. random number배열을 보여줄 js파일생성

random number 배열을 보여줄 파일을 따로 관리용으로 생성해준다.

numlist.js 

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

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

const NumList = props => {
  return (
    <View style={styles.numlist}>
      <Text>{props.num}</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  numlist: {
    backgroundColor: '#bebebe',
    alignItems: 'center',
    padding: 5,
    width: '100%',
    marginTop: 5,
  },
});

export default NumList;

props를 이용해서 App.js가 가진 random배열을 num이라는 변수로 넘겨준다.

(또한, 화면상에서 구분짓기 위해 style에 배경을 회색으로 지정해주었다.->아래에서 확인가능)

 

App.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

import React, {Component} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import Header from './src/header';
import Generator from './src/generator';
import NumList from './src/numlist';

class App extends Component {
  state = {
    appName: 'My First App',
    random: [36, 999],
  };

  onAddRandomNum = () => {
    alert('add random Number!');
  };

  render() {
    return (
		//생략
        <NumList num={this.state.random} />
      </View>
    );
  }
}

export default App;

다음과 같이 NumList를 import해온 후에 num이라는 변수에 random이라는 state를 넘겨준다. 

이렇게 되면, Generator아래 다음과 같이 화면이 뜨며, 넘겨받은 36과 999가 출력됨을 볼 수 있다.

이를 보면 우리가 원하던 출력형식이 아님을 알 수 있다.

배열을 받아올 때는 배열 객체 내장함수를 사용해주어야 한다.

array.map(item, index) 
배열 객체 내장함수 



 arr.map(a,b) 
a는 배열의 value, b는 배열의 index 
let arr = [10,20,30,40,50];

let twice = arr.map((v,i)=>{
    console.log(`i: ${i}, v: ${v}`)
});

console.log(twice);


//결과
i: 0, v: 10
i: 1, v: 20
i: 2, v: 30
i: 3, v: 40
i: 4, v: 50​


이전글 참고
https://iagreebut.tistory.com/124

ES6문법

 

즉, numList에서 random배열을 받아왔을 때 다음과 같이 수정해주어야 한다.

numlist.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

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

const NumList = props => {
  return props.num.map((item, idx) => (
    <View style={styles.numlist}>
      <Text>{item}</Text>
    </View>
  ));
};

const styles = StyleSheet.create({
  numlist: {
    backgroundColor: '#bebebe',
    alignItems: 'center',
    padding: 5,
    width: '100%',
    marginTop: 5,
  },
});

export default NumList;

 

이제 각각이 따로 잘 출력되는 것을 볼 수 있다.

 

6. 함수에 기능부여

App.js에서 임시로 alert를 넣었던 함수내에 난수를 생성하여 state값에 추가시기도록 변경해본다.

App.js

  onAddRandomNum = () => {
    const randomNum = Math.floor(Math.random() * 100) + 1;
    this.setState(prevState => {
      return {
        random: [...prevState.random, randomNum],
      };
    });
  };

Math.random()함수를 이용하여 1~100까지의 난수를 발생시켜 randomNum이라는 변수에 저장한 뒤,

setState를 이용하여, 이전 state(prevState)를 받아와 반영한 뒤, 그 뒤에 생성된 새로운 변수를 추가하여 return해준다.

 

Add Number라는 버튼을 누를 떄 마다 새로운 수가 생성되어 리스트에 반영되어 화면에 출력되는 모습을 볼 수 있다.

 

 

TouchEvent기능부여

이제 추가적으로 "숫자"를 클릭하면, 숫자가 사라지도록 한다.

숫자가 쓰여진 곳은 View 태그와 Text태그로 이루어졌으며, 이를 터치시 사라지는 이벤트가 발생해야하므로,

TouchEvent를 활용해준다

 

1. 숫자의 View tag를 TouchableOpacity로 변경

numlist.js

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

const NumList = props => {
  return props.num.map((item, idx) => (
    <TouchableOpacity
      style={styles.numlist}
      key={idx}
    >
      <Text>{item}</Text>
    </TouchableOpacity>
  ));
};


export default NumList;

TouchableOpacity를 import한 후, View태그를 변경해준다.

 

 

2. Delete기능을 하는 함수를 생성하여 연결 

일단 위와 마찬가지로 임시로 alert를 띄우는 함수를 작성하여 변수를 이용해 numlist로 넘겨준다

App.js

class App extends Component {
  state = {
    appName: 'My First App',
    random: [36, 999],
  };

 //생략

  onDeleteNum = () => {
    alert('delete num');
  };

  render() {
    return (
      <View style={styles.mainView}>
		//생략
        <NumList num={this.state.random} delete={this.onDeleteNum} />
      </View>
    );
  }
}


export default App;

delete라는 변수명으로 onDeleteNum함수를 넘겨준다.

 

이를 numlist에서 props를 이용해 반영해준다.

numlist.js

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

const NumList = props => {
  return props.num.map((item, idx) => (
    <TouchableOpacity
      style={styles.numlist}
      key={idx}
      onPress={() => props.delete()}>
      <Text>{item}</Text>
    </TouchableOpacity>
  ));
};


export default NumList;

 

번호를 누르면 alert창이 뜨게된다.

 

 

3. Delete작용을 하도록 함수 작성 

일단 삭제를 진행하기 위해, 터치된 숫자의 index를 알아야하므로, 

onDeleteNum의 변수로 index를 주어줌 

numlist.js

const NumList = props => {
  return props.num.map((item, idx) => (
    <TouchableOpacity
      style={styles.numlist}
      key={idx}
      onPress={() => props.delete(idx)}>
      <Text>{item}</Text>
    </TouchableOpacity>
  ));
};

일단 numlist에서 index를 보유하고 있으므로, 해당 index를 가리키는 변수인 idx를 

delete의 매개변수로 넣는다.

 

 App.js에서 선언된 onDeleteNum에도 적용해준다. position이라는 변수에 저장한다.

App.js

  onDeleteNum = position => {
    alert('delete num');
  };

 

이제 새로운 array를 생성하여 거기에 원하는 인덱스가 제거된 배열을 만들고 이를 state로 업데이트 시켜주는 방식사용 

  onDeleteNum = position => {
    const newArray = this.state.random.filter((num, index) => {
      return position != index;
    });
  };

배열 내장함수 filter를 사용 : 특정조건에 부합되는 요소들만 뽑아내 새 배열을 만들어내는 함수 

포지션과 인덱스가 일치하지 않는 것들로만 모아 새 배열을 만듬 ( 포지션과 인덱스와 일치하는 것은 없음 -> 제거와 같은 효과) 

이때 , num 과 index를 return하기 때문에, num을 사용하지 않아도 선언해주어야 한다. 

 

setState를 이용하여 새로 만든 array로 변경해준다.

  onDeleteNum = position => {
    const newArray = this.state.random.filter((num, index) => {
      return position != index;
    });

    this.setState({random: newArray});
  };

 

결과)

13 클릭 이전 13 클릭 이후

다음과 같이 숫자를 클릭하면 사라지는 것을 볼 수 있다.

 

 

 

 

전체코드)

App.js 

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

import React, {Component} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import Header from './src/header';
import Generator from './src/generator';
import NumList from './src/numlist';

class App extends Component {
  state = {
    appName: 'My First App',
    random: [36, 999],
  };

  onAddRandomNum = () => {
    const randomNum = Math.floor(Math.random() * 100) + 1;
    this.setState(prevState => {
      return {
        random: [...prevState.random, randomNum],
      };
    });
  };

  onDeleteNum = position => {
    const newArray = this.state.random.filter((num, index) => {
      return position != index;
    });

    this.setState({random: newArray});
  };

  render() {
    return (
      <View style={styles.mainView}>
        <Header name={this.state.appName} />
        <View>
          <Text style={styles.mainText} onPress={() => alert('touch event')}>
            Hello world
          </Text>
        </View>

        <Generator add={this.onAddRandomNum} />
        <NumList num={this.state.random} delete={this.onDeleteNum} />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  mainView: {
    flex: 1,
    backgroundColor: 'white',
    paddingTop: 50,
    alignItems: 'center',
    //justifyContent: 'center',
  },
  subView: {
    backgroundColor: 'yellow',
    marginBottom: 10,
  },
  anotherSubView: {
    flex: 2,
    backgroundColor: 'yellow',
    marginBottom: 10,
    width: '100%',
    alignItems: 'center',
    justifyContent: 'center',
  },
  mainText: {
    fontSize: 20,
    fontWeight: 'normal',
    color: 'red',
    padding: 20,
  },
});

export default App;

 

numlist.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

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

const NumList = props => {
  return props.num.map((item, idx) => (
    <TouchableOpacity
      style={styles.numlist}
      key={idx}
      onPress={() => props.delete(idx)}>
      <Text>{item}</Text>
    </TouchableOpacity>
  ));
};

const styles = StyleSheet.create({
  numlist: {
    backgroundColor: '#bebebe',
    alignItems: 'center',
    padding: 5,
    width: '100%',
    marginTop: 5,
  },
});

export default NumList;

 

generator.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

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

const Generator = props => {
  return (
    <View style={styles.generator}>
      <Button title={'Add Number'} onPress={() => props.add()} />
    </View>
  );
};

const styles = StyleSheet.create({
  generator: {
    backgroundColor: '#FFDFB0',
    alignItems: 'center',
    padding: 5,
    width: '100%',
  },
});

export default Generator;

 

header.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

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

//참고 : jsx component를 return하므로 () => (이부분이 소괄호)

//"TouchableOpacity"
const Header = props => (
  <TouchableOpacity
    style={styles.header}
    onPress={() => alert('On Press')}
    //onLongPress={() => alert('on long press')}
    //onPressIn={() => alert('on press in')}
    //onPressOut={() => alert('on press out')}
  >
    <View style={styles.header}>
      <Text>{props.name}</Text>
    </View>
  </TouchableOpacity>
);

//"TouchableWithoutFeedback"
// const Header = props => (
//   <TouchableWithoutFeedback onPress={() => alert('hello world')}>
//     <View style={styles.header}>
//       <Text>{props.name}</Text>
//     </View>
//   </TouchableWithoutFeedback>
// );

const styles = StyleSheet.create({
  header: {
    backgroundColor: 'pink',
    alignItems: 'center',
    padding: 5,
    width: '100%',
  },
});

export default Header;
728x90