React-3 (23/02/23)

nazzzo·2023년 2월 23일
0

리액트 3번째



1. 생명주기


componentDidMount() : 컴포넌트가 생성되고 첫 렌더링이 완료된 후 호출됩니다
componentDidUpdate() : 컴포넌트가 업데이트된 후 호출됩니다
componentWillUnmount() : 컴포넌트가 제거되기 직전에 호출됩니다


생명주기 메서드에 대한 이해를 돕기 위해
먼저 리액트로 간단한 카운터 기능을 만들어보겠습니다

    <div id="root"></div>
    <script type="text/babel">
        // Counter 컴포넌트를 만들어서 상태 관리하기
        class Counter extends React.Component {
            constructor(props) {
                super(props);

                // 컴포넌트가 생성될 때(생성자함수) 상태를 가지고 시작
                this.state = {
                    number: 0,
                }
            }
            // 첫 렌더링이 완료된 후 10이라는 상태를 가지게 됩니다
            componentDidMount() {
                // setState가 render 함수를 재호출합니다
                this.setState({ number: 0 })
            }
            render() {
                return (
                    <div>
                        <h2>{this.state.number}</h2>
                        <button>+</button>
                        <button>-</button>
                    </div>
                )
            }
        }
        class App extends React.Component {
            constructor(props) {
                super(props);
            }
            render() {
                return (
                    <div>
                        <Counter />
                    </div>
                )
            }
        }
        const root = ReactDOM.createRoot(document.getElementById("root"))
        root.render(<App />)
    </script>

사용자가 아무런 조작을 하지 않았음에도 위 코드는 화면 렌더링이 2번 실행된 것입니다

별 반 차이가 없어보이지만 생성자 함수가 state 데이터를 가지고 시작하는 것보다
초기 렌더링을 끝낸 뒤 생성주기 메서드를 사용해서 데이터를 불러오는 쪽을 더 추천합니다
사용자에게 먼저 html 틀을 보여주고, 데이터는 따로 받아오는 편이 더 나은 사용성을 제공하기 때문입니다

            componentDidMount() {
                // setState가 render 함수를 재호출합니다
                setTimeout(() => {
                    this.setState({ number: 10 })
                }, 3000)
            }


            render() {
                return (
                    <div>
                        <h2>{this.state.number === 0 ? "로딩중" : this.state.number}</h2>
                        <button>+</button>
                        <button>-</button>
                    </div>
                )
            }
        }

↑ 앞으로는 이러한 형태의 코드를 자주 보게 될 것...



아래는 완성코드입니다

<body>
    <div id="root"></div>
    <script type="text/babel">
        // Counter 컴포넌트를 만들어서 상태 관리하기
        class Counter extends React.Component {
            constructor(props) {
                super(props);

                // 컴포넌트가 생성될 때(생성자함수) 상태를 가지고 시작
                this.state = {
                    number: 0,
                    isLoading: true,
                }
                this.increment = this.increment.bind(this)
            }
            // 첫 렌더링이 완료된 후 10이라는 상태를 가지게 됩니다
            componentDidMount() {
                // setState가 render 함수를 재호출합니다
                setTimeout(() => {
                    this.setState({ number: 0, isLoading: false })
                }, 1000)
            }
            // 상태가 바뀌면 호출될 메서드
            componentDidUpdate() {
                console.log(this.state.number)
            }

            increment() {
                this.setState({
                    number: this.state.number + 1,
                })
            }
            decrement = () => {
                this.setState({
                    number: this.state.number - 1,
                })
            }
            render() {
                if (this.state.isLoading) return <div>로딩중</div>
                return (
                    <div>
                        <h2>{this.state.isLoading? "로딩중" : this.state.number}</h2>
                        <button onClick={this.increment}>+</button>
                        <button onClick={this.decrement}>-</button>
                    </div>
                )
            }
        }

        class App extends React.Component {
            constructor(props) {
                super(props);
            }
            render() {
                return (
                    <div>
                        <Counter />
                    </div>
                )
            }
        }
        const root = ReactDOM.createRoot(document.getElementById("root"))
        root.render(<App />)
    </script>
</body>

렌더 메서드 실행 → ComponentDidMount로 데이터를 불러오면 렌더 메서드 재실행 →
클릭 이벤트 실행 → ComponentDidUpdate로 상태 변경 → 렌더 메서드 재실행

*이후 생명주기 메서드 안에는 axios와 같은 비동기 요청 코드가 담길 예정입니다



2. SPA 맛보기


리액트는 기본적으로 싱글 페이지 어플리케이션의 구현을 위해 제작된 라이브러리입니다
이를 위해 하나의 html 파일에서, 각 컴포넌트의 상태(데이터)가 바뀐 부분만을 찾아 화면을 다시 그리는 방식으로 구조가 짜여있습니다

그런데 <form> 태그의 submit과 같은 기능은 데이터를 전송할 때 새로고침 현상을 발생시키고
또 자바스크립트의 코드는 페이지의 새로고침이 발생하면 모든 이벤트의 실행 결과가 초기화된다는 문제가 있습니다

아래 예제 코드를 통해 해당 이슈에 대해 파악해보겠습니다


<body>
    <div id="root"></div>
    <script type="text/babel">
        class Form extends React.Component {
            constructor(props) {
                super(props)
                this.state = {
                    value: "",
                    word: "",
                }
            }
            // onChange는 엘리먼트의 상태가 바뀔 때마다 발동된다는 것을 이용합니다
            handleChange = (e) => {
                this.setState({ value: e.target.value })
            }
            handleSubmit = (e) => {
                // SPA 구현을 위해 form 태그의 새로고침을 막아두고 시작해야 합니다, 자주 사용하게 될 패턴
                e.preventDefault()
                this.setState({
                    word: this.state.value,
                    value: "",
                })
            }

            render() {
                return (
                    <form onSubmit={this.handleSubmit}>
                        <input type="text" onChange={this.handleChange} value={this.state.value} />
                        <button type="submit">전송</button>
                        <div>{this.state.word}</div>
                    </form>
                )
            }
        }

        class List extends React.Component {
            constructor(props) {
                super(props);
                this.state = {
                    board: [
                        { id: 1, subject: 'web7722' },
                        { id: 1, subject: 'web7733' },
                        { id: 1, subject: 'web7744' },
                        { id: 1, subject: 'web7755' },
                    ]
                }
            }
            getList(board) {
                return board.map((val, key)=><li key={key}>{val.subject}</li>)
            }
            render() {
                return <ul>{this.getList(this.state.board)}</ul>
            }
        }

        class App extends React.Component {
            constructor(props) {
                super(props);
            }
            render() {
                return (
                    <div>
                        <Counter />
                        <Form />
                        <List />
                    </div>
                )
            }
        }
        const root = ReactDOM.createRoot(document.getElementById("root"))
        root.render(<App />)
    </script>
</body>


  • 배열 메서드 중에서도 map() 매서드만큼은 사용법을 제대로 파악하고 있어야 합니다
  • 위 코드에서 <form> 태그의 input value를 상태로 관리하고,
    submit을 동작시키는 구현 방식은 앞으로도 자주 사용하게 될 패턴입니다



3. props 전달 연습하기


위 화면에서 Props : web7722는 어떤 과정을 거쳐서 전달되었을까요?


<body>
    <div id="root"></div>
    <script type="text/babel">
        class C extends React.Component {
            constructor(props) {
                super(props);
            }

            render() {
                return <div>
                        Props: {this.props.text}
                    </div>
            }
        }

        class B extends React.Component {
            constructor(props) {
                super(props);
            }

            render() {
                return <div>
                        <C text={this.props.id} />
                    </div>
            }
        }

        class A extends React.Component {
            constructor(props) {
                super(props);
            }

            render() {
                return <div>
                        <B id={this.props.name} />
                    </div>
            }
        }

        class Props extends React.Component {
            constructor(props) {
                super(props);
            }

            render() {
                return <div>
                        <A name="web7722" />
                    </div>
            }
        }

        class App extends React.Component {
            constructor(props) {
                super(props);
            }
            render() {
                return (
                    <div>
                        <Counter />
                        <Form />
                        <List />
                        <Props />
                    </div>
                )
            }
        }
        const root = ReactDOM.createRoot(document.getElementById("root"))
        root.render(<App />)
    </script>
</body>

<A name="web7722" /><B id={this.props.name} />

<B id={this.props.name} /><C text={this.props.id} />

<C text={this.props.id} /><div>Props: {this.props.text}</div>


컴포넌트명과 변수명을 바꿔가며 자주자주 연습합시다

*컴포넌트의 innerHTML은 children이라는 속성명으로 고정되어 전달됩니다



4. 정리

  • Component는 엘리먼트의 모음이며 하나의 상태만을 가질 수 있습니다. 상태에 따라 엘리먼트가 바뀔 수 있습니다

  • Props는 데이터(값)의 전달이며, 방향은 자식 컴포넌트를 향해 한방향으로만 이어집니다

  • State(상태)는 엘리먼트에 표현할 데이터를 모아주는 변수를 뜻합니다

  • State 끌어올리기를 사용하려면 부모 컴포넌트와 자식 컴포넌트를 구분할 수 있어야 합니다,
    그리고 함수이라는 사실도 알아야 합니다
    (부모 컴포넌트에서 본인의 상태를 바꾸는 함수를 미리 구현 → 자식 컴포넌트는 전달받은 함수를 실행)

  • 생명주기는 상태 변화에 따라 실행할 추상 메서드를 뜻합니다
    (this.setState()를 사용할 경우에 componentDidUpdate()render()가 실행되는 상황)

  • 이벤트는 이벤트를 등록하는 방법에 대해서만큼은 정확히 인지해야합니다,
    this 바인딩에 대해 공부하는 것도 자바스크립트 작동원리 이해에 큰 도움이 됩니다

  • 조건부 렌더링 ~ JSX 문법({})과 삼항연산자를 통한 컴포넌트 호출

  • List 렌더링 ~ 같은 엘리먼트를 여러번 사용할 때, 배열[]에 담아서 사용해도 출력된다는 것을 알아둡시다

<ul>
  {[<li>hello</li>,<li>world</li>]}
</ul>

map() 메서드를 사용해서 배열 내용을 변경할 수 있으면 OK입니다

const arr = [{a: 10},{b: 20}]
// map 함수로 그리기
[<li>a: 10</li>, <li>b: 10</li>]

0개의 댓글