국비교육(22-23)

43일차(2)/React(3) : map, concat 함수 / 이벤트 처리 예제

서리/Seori 2022. 12. 8. 00:49

43일차(2)/React(3) : map, concat 함수 / 이벤트 처리 예제

 

- 매번 새 프로젝트를 생성하려면 시간이 오래 걸리므로...

App00.js 를 만든 후 해당 파일을 브라우저에 출력하도록 바꾸는 방식으로 실습

 

 

App02.js

//App02.js

import { Component } from "react";

class App02 extends Component{
    //상태값(state) 정의하기
    state={
        info:"x:0, y:0",
        info2:"x:0, y:0"
    };

    render(){
        //요소에 적용할 인라인 css를 object로 정의하고 적용할 수 있다.
        const boxStyle={
            width:"300px",
            height:"300px",
            border:"1px solid red",
            backgroundColor:"yellow" //여러 단어로 구성된 속성은 camel case를 사용한다.
        };
      
        return(
            <div className="container">
                <h3>마우스 이벤트 처리</h3>
                {/* 
                    이벤트리스너 함수에 전달되는 이벤트 객체는 react가 넣어주는 이벤트 객체이다.
                    original 이벤트 객체를 사용하고 싶다면
                    e.nativeEvent 를 사용하면 된다.
                */}
                <div style={boxStyle} onMouseMove={(e)=>{
                    console.log(e);
                    //마우스의 좌표를 이용해서 문자열을 만들어낸 다음
                    let info=`x:${e.clientX}, y:${e.clientY}`;
                    //state를 update한다.(state를 사용하는 UI는 자동 update 된다.)
                    this.setState({info:info});
                }}></div>
                {/* state 값을 활용해서 출력하기 */}
                <p>{this.state.info}</p>

                {/* 이벤트 처리를 할 함수를 미리 만들어 놓고 이벤트 리스너 함수로 등록하기 */}
                <div style={boxStyle} onMouseMove={this.handleMouseMove}></div>
                <p>{this.state.info2}</p>
            </div>
        );
    }//render()

    //mouseMove 이벤트를 처리할 함수
    handleMouseMove = (e)=>{
        //마우스의 좌표를 이용해서 문자열을 만들어낸 다음
        let info=`x:${e.clientX}, y:${e.clientY}`;
        //state를 update한다.(state를 사용하는 UI는 자동 update 된다.)
        //여기서 this가 App2 Component를 참조하게 하려면 이 함수는 화살표함수로 만들어져 있어야 한다.
        this.setState({info2:info});
    }

}//class App02

export default App02;

- 마우스 이벤트 처리하기

 

 

- src에 새로 만든 app02가 화면에 나오게 하려면?

→ 밑에 export default App02; 추가하고 index.js에서 App02로 수정

 

onMouseMove{(e)=>{ }}

- 함수명은 카멜케이스로 적어주기

- 이벤트객체 e를 함께 전달

 

 

- 이벤트객체 e가 콘솔에 인쇄되는데, react에서 한번 조작한 이벤트객체가 출력된다.

- 여기에는 offSetX,Y는 없다

 

- react에서 한번 조작한것이기 때문에 그렇다.

- offSetX,Y는 nativeEvent 안에 있으므로, 원본 이벤트객체를얻고 싶다면 nativeEvent 를 사용하면 된다.

 

- 이 마우스 포인터의 좌표값을 출력하려면 model이 필요하다

- react에서는 모델을 state { } 에서 관리한다.

 

- state를 정의한다. state 안의 info라는 방에 변수 x, y 를 넣어준다.

- render 바깥에다가 정의해야 한다!

 

- 이것을 render 안에서 {this.state.info} 형태로 출력하면 된다.

- 현 상태에서는 이렇다! offset 값이 적용되지는 않음

 

- {this.state.info} 출력하기 전에

  info 안의 문자열을 재지정하고, state를 업데이트해주면 된다.(UI는 자동 업데이트됨)

 

 

- state에서 초기값을 0으로 지정하고, 그 값을 계속 바꿔주는 형태로 작성!

 

- component 안의 setState 함수는 

 기존에 있는 info(모델의 값)를 새로운 info 로 업데이트하는 함수이다.

 


 

- 이벤트 처리를 할 메소드를 미리 만들어 놓고 이벤트 리스너 함수로 등록하기 

 

- 함수명을 만들어놓고 새 함수를 아래에서 등록하기

- render 바깥에서, class App02 안쪽에서 함수를 만들어야 한다.

 

- handleMouseMove 함수생성(위와 같은 내용)

 

- 박스2 안에서 마우스 움직임이 일어나면 info에서 읽어와서 info2가 state를 업데이트하게된다.

 

handleMouseMove(e){ }

- 그러나! 이렇게 만들면 this가 가리킬 수가 없다.

 

- render는 (){} 으로 만들어도 되지만 다른 함수를 만들 때에는 화살표함수로 만드는 것이 좋다.

- (e)=>{ } 가 handleMouseMove 안에 대입되는 구조이다.

- handleMouseMove가 하나의 필드이고, 여기에 함수가 들어간 것이라고 생각하면 된다.

 


 

App03.js

//App03.js

import { Component } from "react";

class App03 extends Component{
    render(){
        /*
            배열에 jsx객체를 여러개 넣기
        */
        const names=[];
        names.push(<li key={0}>바나나</li>);
        names.push(<li key={1}>딸기</li>);
        names.push(<li key={2}>복숭아</li>);

        //jsx객체를 만들어낼 아이템이 배열에 준비되어 있다고 가정하자
        const animals=["강아지", "코끼리", "고양이"];

        //만들어낼 jsx 객체를 저장할 빈 배열을 만든다.
        const result=[];
        //반복문 돌면서
        for(let i=0; i<animals.length; i++){
            //동물 이름이 출력된 jsx객체를 만들어서
            let tmp=<li key={i}>{animals[i]}</li>;
            //미리 준비된 배열에 누적시키기
            result.push(tmp);
        }
        //배열의 map() 함수를 활용해서 jsx객체가 들어있는 새로운 배열을 얻어내기
        const result2= animals.map((item, index)=>{
            return <li key={index}>{item}</li>
        });

        return(
            <div className="container">
                <h3>반복문 돌면서 여러 개의 문서 객체 만들기</h3>
                <ul>
                    {names}
                </ul>
                <h3>동물 목록</h3>
                <ul>
                    {result}
                </ul>
                <h3>동물 목록2</h3>
                <ul>
                    {result2}
                </ul>
            </div>
        );

    }

}

export default App03;

 

- names배열을 만들고, names.push() 안에 <li>를 넣어서 배열에 담는다.

 

- 출력은 되지만, 콘솔창에 각각의 child는 key property가 필요하다는 경고 창이 뜬다.

 

- 인덱스 키를 넣어줌. 만약 DB라면 primary key를 넣어주면 될 것

 

const result=[];
for(let i=0; i<animals.length; i++){
    result.push(<li key={i}>{animals[i]}</li>)
}
const result=[];
//반복문 돌면서
for(let i=0; i<animals.length; i++){
    //동물 이름이 출력된 jsx객체를 만들어서
    let tmp=<li key={i}>{animals[i]}</li>;
    //미리 준비된 배열에 누적시키기
    result.push(tmp);
}

- 같은것이다. 첫번째 작성 방식에 익숙해지기

 

- map() : 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환하는 함수

- map() 이라는 함수는 새로운 배열을 리턴해준다.

 

- Array를 사용해서 새로운 배열(map1)을 얻어낸다. 기존 Array의 아이템을 활용해서!

- 각각의 아이템을 어떻게 활용할지 작성하면 된다.

 

- 위 예제는 2를 곱한 값을 이용해서 새로운 배열을 만들어낸다는 뜻!

 

*map 설명 : 링크

 

- map1과 map2의 작성법 비교. 함수 안에 따로 로직이 없으면 리턴할 값만 들어가면 된다.

 

- (item, index)로 입력하면 아이템과 인덱스까지 함게 전달할 수 있다.

 

<li key={ }>

- react에서 배열을 출력하려면 하나마다 유일한 키값이 부여되어야 한다.

 

- map 함수 중요하다! 새로운 배열을 얻어낸다는 점에 유의하기

 


 

App03_ex1.js

//App03_ex1.js

import { Component } from "react";

class App extends Component{
    state={
        index:0,
        msgs:[]
    };
    render(){
        return(
            <div className="container">
                <h1>문서 객체 동적으로 만들기</h1>
                <input type="text" onKeyUp={this.handleKeyUp} />
                <ul>
                    {this.state.msgs}
                </ul>
            </div>
        );

    }
    //input요소에 KeyUp이벤트가 일어날 때마다 호출되는 함수
    handleKeyUp=(e)=>{
        console.log(e);
        //만일 Enter Key를 눌렀다면
        if(e.keyCode==13){
            //입력한 문자열 읽어오기(이벤트가 일어난 input 요소의 value 값)
            let msg=e.target.value;
            //아래와 같이 배열에 아이템을 추가한다고 해서 UI가 update되지는 않는다.
            //this.state.msgs.push(<li>msg</li>)

            //아이템이 추가된 새로운 배열을 얻어내서 setState() 해야 한다.
            let newArray = this.state.msgs.concat(<li key={this.state.index}>{msg}</li>);
            this.setState({
                msgs:newArray,
                index:this.state.index+1
            });
        }
    }
}

export default App;

 

<input type="text" onInput={this.handleInput} />

- input요소에서 뭔가 입력되었을 때 this.handleInput 함수가 호출되도록!

 

- 이벤트 객체 전달되게 하기

 

- 그런데 이렇게 하면 엔터는 작동되지 않는다. 입력되는 것이 아니라서...

 

<input type="text" onKeyUp = {this.handleKeyUp} />

- 이럴 땐 onInput 대신 onKeyUp을 사용

 

- Enter도 인식되는 것을 확인할 수 있다.

 

- 콘솔에서 Enter의 키코드가 13번이라는 것을 알아낼 수 있다.

 

- input 요소가 뭔가를 입력하고 엔터를 치면 입력한 내용을 msgs의 배열에 추가하도록 할 예정!

 

- 이렇게 작성하면 추가가 되지 않는다. UI가 업데이트되지 않는다.

(vue의 경우 배열의 item이 수정되면 UI에 자동 반영이었는데, react에서는 아니다)

 

- 구글에서 javascript 배열 concat 검색

* concat() 함수 설명 : 링크

 

 

- 2개의 배열을 합치면 새로운 배열이 나온다.

 

- 배열이 아닌, 아이템만 넣어도 적용된다. (array2)

 

if(e.keyCode==13){    
    let msg=e.target.value;
    
    let newArray = this.state.msgs.concat(<li>{msg}</li>);
    this.setState({msgs:newArray});
}

- concat 안에 msg를 리스트로 만들어서 출력.

 

- 엔터를 누르면 배열에 입력되긴 하는데 unique key 값이 없어서 경고 발생

 

if(e.keyCode==13){    
    let msg=e.target.value;
    let newArray = this.state.msgs.concat(<li key={this.state.index}>{msg}</li>);
    this.setState({
        msgs:newArray,
        index:this.state.index+1
    });
}

- this.state.index를 넣어주고, setState할 때 msgs뿐만 아니라 index도 하나씩 값을 증가시켜주면 된다.

 

 

- 엔터 할때마다 새로운 리스트가 생겨난다.

 

- state에 들어 있는 내용은 이 object의 특정 방에 있는 내용을 끊임없이 새로운 값으로 교체하는 느낌이다.

- .setState() 를 사용해 state에 새로운 데이터를 담으면 UI는 자동으로 업데이트 된다.

 

 


 

App03_ex2.js

- input요소에서 값 받아서 전달하기

//App03_ex2.js

import { Component } from "react";

class App extends Component{

    state={
        index:0,
        msgs:[]
    }

    render(){

        return(
            <div className="container">
                <h1>동적으로 문서 객체 만들기2</h1>
                {/* 
                    input 요소가 초기화되는 시점에 ref={ 함수 } 안에 있는 함수가 호출되면서
                    input 요소의 참조값이 함수의 매개변수에 전달된다.
                    매개변수에 전달된 값을 필드에 저장하면 추후에 필요한 시점에 사용할 수 있다.
                */}
                <input type="text" placeholder="메세지 입력" ref={(ref)=>{
                    console.log(ref);
                    //ref 에 담긴 참조값을 inputText라는 이름의 필드에 저장하기
                    this.inputText=ref;
                }}/>
                <button onClick={this.handleClick}>전송</button>
                <ul>
                    {this.state.msgs}
                </ul>
            </div>
        );
    }
    //버튼을 눌렀을때 호출되는 함수
    handleClick = ()=>{
        //입력한 메세지 읽어오기
        let msg=this.inputText.value;
        let newArray=this.state.msgs.concat(<li key={this.state.index}>{msg}</li>)
        //상태값을 변경해서 UI를 업데이트한다.
        this.setState({
            index:this.state.index+1,
            msgs:newArray
        });
    }
}

export default App;

 

- 이번에는 input요소가 아니라 버튼 요소에 이벤트가 일어났을때 input 요소에 들어온 값을 읽어오는 것.

- 일반 javascript라면 document.querySeletor("id") 로 읽어오겠지만,

  react에서는 component에 id를 부여하는 것은 좋지 않다.

- 그러면 어떻게 읽어오는지? → ref 를 사용한다.

 

- ref : 레퍼런스 값과 관련된 react의 기능

 

- 이 input 요소가 초기화될때 이 input 요소 안의 참조값이 전달되면서 함수가 호출된다.

 

 

- 새로고침 해보면 뭔가 console 창에 전달되는 것을 볼 수 있다. 아직 아무것도 입력하지 않았어도!

- UI가 초기화되는 시점에 ref 라는 함수에 input 요소의 참조값이 전달된다.

 

 

- 다른 곳에서 이 input요소의 참조값이 필요하다면 this.inputText 하면 된다.

 아까 ref가 저 안에 저장해놨기 때문에!

 

- ref 를 사용해서 담아놓은 것을 아래에서 호출해서 msg안에 담아주기!

- state 안에 index:0, msgs:[] (빈 배열)을 준비해주고, <ul> 안에 msgs를 출력한다.

- 마지막으로 this.setState를 사용해 state 값을 업데이트해주면 된다.