[React] 1일차 / State, Babel, 함수형 setState, ref, 렌더링 타이밍

선주·2022년 1월 14일
0

React

목록 보기
1/2
post-thumbnail

1. State

화면에서 바뀌는 부분을 State라고 한다.
class 안에 state = {};로 정의해주고, 중괄호 안에 바뀌는 부분들을 넣는다.
state는 setState로 우리가 수동으로 변경해줄 값만 넣어야 한다. 자동적으로 바뀌는 값 X

예시

class GuGuDan extends React.Component {
    state = {
        first: Math.ceil(Math.random() * 9), 
        second: Math.ceil(Math.random() * 9),
        value: '',
        result: '',
        answer: ''
    };
}

2. Babel

JavaScript에서 실험적인 코드를 사용할 수 있게 해 주는 컴파일러
최신문법을 사용해 코딩하되 배포시에 구버전으로 트랜스컴파일해서 출시할 수 있도록 한다.

사용 예시

<html>
    <head>
        <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
        <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
        <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    </head>
    <body>
        <div id="root"></div>
        <script type="text/babel">
            // 컴포넌트 생성
            class LikeButton extends React.Component {
                constructor(props) {
                    super(props);
                    this.state = {
                        liked: false,
                    };
                }
                // 어떻게 화면에 표시할 것인지 결정하는 메소드
                // <button>Like</button>이라는 태그를 만들겠다
                render() {
                    return <button type="submit" onClick={() => { this.setState({liked: true})}}>
                    {this.state.liked ? 'Liked' : 'Like'}
                    </button>;
                }
            }
        </script>
        <script type="text/babel">
            // LikeButton을 실제 Dom의 root 안에 붙이겠다
            ReactDOM.render(<LikeButton/>, document.querySelector('#root'));
        </script>
    </body>
</html>

babel 스크립트를 넣어주고 타입을 text/babel로!
여기서 JSX(JavaScript+XML)가 나온다. 컨텐트 부분에 중괄호로 감싸서 JS 문법을 또 쓸 수 있다.

JSX의 좋은 점

  • 코드를 보자마자 '이건 태그구나'하고 바로 알아차릴 수 있다.
  • 컴포넌트를 원하는 갯수만큼 쉽게 만들 수 있다. ⇒ 재사용성 Good

ReactDOM.render(<div><LikeButton/><LikeButton/><LikeButton/><LikeButton/></div>, document.querySelector('#root'));

vs

ReactDOM.render(<div>
	<button onClick={onclick}>Like</button>
	<button onClick={onclick}>Like</button>
	<button onClick={onclick}>Like</button>
	<button onClick={onclick}>Like</button>
</div>, document.querySelector('#root');

3. 의미없는 div 태그 지우기

render(){
    return (
        <div>
            <div>{this.state.first}곱하기{this.state.second}?</div>
            <form onSubmit={this.onSubmit}>
                <input type="number" value={this.state.value} onChange={this.onChange} />
                <button>입력!</button>
            </form>
            <div>{this.state.answer}{this.state.result}</div>
        </div>
    );
}

return을 위해 모든 태그를 감싸는 div태그가 존재하는데, 개발자도구에서 보면 이 div가 의미없이 자리만 차지하고 있는 것이 보인다. 이 div를 지우고 return도 정상적으로 되도록 바꿔보자.

render(){
    return (
        <React.Fragment>
            <div>{this.state.first}곱하기{this.state.second}?</div>
            <form onSubmit={this.onSubmit}>
                <input type="number" value={this.state.value} onChange={this.onChange} />
                <button>입력!</button>
            </form>
            <div>{this.state.answer}{this.state.result}</div>
        </React.Fragment>
    );
}

return도 정상작동, 의미없이 자리만 차지하던 div도 제거된 모습이다.


4. 함수형 setState

if(parseInt(this.state.value) === this.state.first * this.state.second){
    this.setState({
        result: '정답: ' + this.state.value,
        first: Math.ceil(Math.random()*9),
        second: Math.ceil(Math.random()*9),
        value: '',
    });
}

현재 setState를 보면, this.state.value는 현재의 값인데 result, first, second, value는 미래에 이렇게 바꿀거라는 미래의 값이다. 이 부분에 대한 구별 없이 코드를 작성하면 나중에 헷갈릴 위험이 있다.

if(parseInt(this.state.value) === this.state.first * this.state.second){
    this.setState((prevState) => {
        return {
            result: '정답: ' + prevState.value,
            first: Math.ceil(Math.random()*9),
            second: Math.ceil(Math.random()*9),
            value: '',
        };
    });
}

setState 안에 함수를 넣고 그 안에서 return을 시켜보자. 즉, 새로운 state를 리턴하는 것. prevState에는 바꾸기 전의 상태값이 들어있다. 그 값을 다음 상태값에 활용할 수 있다. 자주 쓰이는 방식이니 알아두기!

예전 state의 값으로 새로운 state를 만들 때는 함수형 setState를 사용하자!


5. ref

각각 입력버튼을 누르기 전과 후의 모습이다.

render(){
    return (
        <React.Fragment>
            <div>{this.state.first}곱하기{this.state.second}?</div>
            <form onSubmit={this.onSubmit}>
                <input type="number" value={this.state.value} onChange={this.onChange} />
                <button type="submit">입력!</button>
            </form>
            <div>{this.state.result}</div>
        </React.Fragment>
    );
}

현재는 [입력!]버튼을 누르면 input에 focus가 풀리는 상황이다. 버튼을 눌러도 focus가 풀리지 않도록 바꿔보자.

input;

render(){
    return (
        <React.Fragment>
            <div>{this.state.first}곱하기{this.state.second}?</div>
            <form onSubmit={this.onSubmit}>
                <input ref={(c)=> { this.input = c; }} type="number" value={this.state.value} onChange={this.onChange} />
                <button type="submit">입력!</button>
            </form>
            <div>{this.state.result}</div>
        </React.Fragment>
    );
}

DOM에 직접 접근하고 싶을 때는 ref 속성을 붙여서 접근하면 된다. 이 경우도 DOM에 직접 접근하는 것이므로 input 태그에 ref 속성을 붙이자. ref={(c)=> { this.input = c; }} 는 그냥 구문처럼 외울 것! 그리고 input은 클래스에 변수로 선언해준다. 변수명으로 쓰인 input은 임의로 이름을 변경해도 상관없다. (ex. hello)

onSubmit = (e)=>{
    e.preventDefault();
        if(parseInt(this.state.value) === this.state.first * this.state.second){
            this.setState((prevState) => {
                return {
                    result: '정답: ' + prevState.value,
                    first: Math.ceil(Math.random()*9),
                    second: Math.ceil(Math.random()*9),
                    value: '',
                };
            });
            this.input.focus();
        } else {
              this.setState({
                  result: "땡",
                  value: '',
              });
              this.input.focus();
        }
};

그러면 이렇게 input에 직접 접근할 수 있고, this.input.focus()로 이 input에 focus를 주면 끝!

각각 입력버튼을 누르기 직전과 직후의 모습이다. 입력버튼을 눌러도 입력창에 focus가 유지되고 있다.


6. 렌더링 타이밍

render() 함수는 어느 시점에 실행될까? render() 에 console.log('렌더링');을 추가해 알아보았다.

setState가 실행될 때마다 render()도 재실행된다. 성능 최적화를 위해서는 꼭 알아둬야 할 부분!

따라서 render()에는 복잡한 함수를 최대한 넣지 않는 것이 좋다.

input;

render(){
    return (
        <React.Fragment>
            <div>{this.state.first}곱하기{this.state.second}?</div>
            <form onSubmit={this.onSubmit}>
                <input ref={(c)=> { this.input = c; }} type="number" value={this.state.value} onChange={this.onChange} />
                <button type="submit">입력!</button>
            </form>
            <div>{this.state.result}</div>
        </React.Fragment>
    );
}

↓ ref속성도 밖으로 이렇게 빼주자.

input;

onRefInput = (c) => {
    this.input = c;
}

render(){
    return (
        <React.Fragment>
            <div>{this.state.first}곱하기{this.state.second}?</div>
            <form onSubmit={this.onSubmit}>
                <input ref={this.onRefInput} type="number" value={this.state.value} onChange={this.onChange} />
                <button type="submit">입력!</button>
            </form>
            <div>{this.state.result}</div>
        </React.Fragment>
    );
}



최종 구구단 웹게임 코드

<html>
    <head>
        <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
        <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
        <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    </head>
    <body>
        <div id="root">
            
        </div>
        <script type="text/babel">
            class GuGuDan extends React.Component {
                state = {
                    first: Math.ceil(Math.random() * 9), 
                    second: Math.ceil(Math.random() * 9),
                    value: '',
                    result: '',
                };

                onSubmit = (e)=>{
                    e.preventDefault();
                    if(parseInt(this.state.value) === this.state.first * this.state.second){
                        this.setState((prevState) => {
                            return {
                                result: '정답: ' + prevState.value,
                                first: Math.ceil(Math.random()*9),
                                second: Math.ceil(Math.random()*9),
                                value: '',
                            };
                        });
                        this.input.focus();
                    } else{
                        this.setState({
                            result: "땡",
                            value: '',
                        });
                        this.input.focus();
                    }
                };

                onChange = (e) => {
                    this.setState({value:e.target.value});
                };

                onRefInput = (c) => {
                    this.input = c;
                }

                input;

                // 컨텐츠
                render(){
                    return (
                        <React.Fragment>
                            <div>{this.state.first}곱하기{this.state.second}?</div>
                            <form onSubmit={this.onSubmit}>
                                <input ref={this.onRefInput} type="number" value={this.state.value} onChange={this.onChange} />
                                <button type="submit">입력!</button>
                            </form>
                            <div>{this.state.result}</div>
                        </React.Fragment>
                    );
                }
            }
        </script>
        <script type="text/babel">
            ReactDOM.render(<GuGuDan />, document.querySelector('#root'));
        </script>
    </body>
</html>
profile
기록하는 개발자 👀

0개의 댓글