React/NodeBird(ZeroCho)

리렌더링 이해하기

느리지만 꾸준하게 2021. 9. 17. 15:32

비밀번호와 로그인이 너무 붙어있다. 여유롭게 스타일 해주자.

여기서 주의할 점,,

style에다가 {} 객체를 집어넣으면 안된다. LoginForm이 리렌더링 될 때마다 아래 LoginForm함수가 통째로 실행이 되는데 객체끼리 비교하면 false가 나온다.

// LoginForm.js

const LoginForm = () => {
  const [id, setId] = useState('');
  const [password, setPassword] = useState('');

  const onChangeId = useCallback((e) => {
    setId(e.target.value);
  }, [])

  const onChangePassword = useCallback((e) => {
    setPassword(e.target.value);
  }, [])

  return (
    <Form>
      <div>
        <label htmlFor="user-id">아이디</label>
        <br />
        <Input name="user-id" value={id} onChange={onChangeId} required />
      </div>
      <div>
        <label htmlFor="user-password">비밀번호</label>
        <br />
        <Input 
          name="user-password"
          type="password"
          value={password}
          onChange={onChangePassword} 
          required 
        />
      </div>
      <div style={{ marginTop: 10 }}>
        <Button type="primary" htmlType="submit" loading={false}>로그인</Button>
        <Link href="/signup"><a>회원</a></Link>
      </div>
    </Form>
  );
}
{} === {} // false

즉 아래 부분 때문에

<div style={{ marginTop: 10 }}>

div 태그 전체가 리렌더링이 된다. 리액트는 VirtualDOM으로 검사를 하면서 어디가 달라졌는지 찾다가 {} 객체가 다르구나라고 인식하고 div태그 전체를 리렌더링 하는 것이다.

      <div style={{ marginTop: 10 }}>
        <Button type="primary" htmlType="submit" loading={false}>로그인</Button>
        <Link href="/signup"><a>회원</a></Link>
      </div>

 

styled-components를 사용해보자.

 

styled-components를 불러와서 styled.div를 통해 ButtonWrapper를 div태그로 만들고 스타일을 넣는 것이다.

// LoginForm.js
import React, { useCallback, useState } from 'react';
import { Button, Form, Input } from 'antd';
import Link from 'next/link';
import styled from 'styled-components';

const ButtonWrapper = styled.div`
  margin-Top: 10px;
  
`


const LoginForm = () => {
  const [id, setId] = useState('');
  const [password, setPassword] = useState('');

  const onChangeId = useCallback((e) => {
    setId(e.target.value);
  }, [])

  const onChangePassword = useCallback((e) => {
    setPassword(e.target.value);
  }, [])

  return (
    <Form>
      <div>
        <label htmlFor="user-id">아이디</label>
        <br />
        <Input name="user-id" value={id} onChange={onChangeId} required />
      </div>
      <div>
        <label htmlFor="user-password">비밀번호</label>
        <br />
        <Input 
          name="user-password"
          type="password"
          value={password}
          onChange={onChangePassword} 
          required 
        />
      </div>
      <ButtonWrapper>
        <Button type="primary" htmlType="submit" loading={false}>로그인</Button>
        <Link href="/signup"><a>회원</a></Link>
      </ButtonWrapper>
    </Form>
  );
}

export default LoginForm;

 

레이아웃 파일도 고쳐보자. style 안에다가 Input 컴포넌트를 넣어준다.

import styled from 'styled-components';

const SearchInput = styled(Input.Search)
// LoginForm.js

import React, { useState } from 'react';
import PropTypes from 'prop-types';
import Link from 'next/link';
import { Menu, Input, Row, Col } from 'antd';
import styled from 'styled-components';

import UserProfile from '../components/UserProfile';
import LoginForm from '../components/LoginForm';

const SearchInput = styled(Input.Search)`
    vertical-align: middle;
`;


const AppLayout = ({ children }) => {
    const [isLoggedIn, setIsLoggedIn] = useState(false);
    return(
        <div>
            <Menu mode="horizontal">
                <Menu.Item>
                    <Link href="/"><a>노드버드</a></Link>
                </Menu.Item>
                <Menu.Item>
                    <Link href="/"><a>프로필</a></Link>
                </Menu.Item>
                <Menu.Item>
                    <SearchInput enterButton />
                </Menu.Item>
                <Menu.Item>
                    <Link href="/"><a>회원가입</a></Link>
                </Menu.Item>
            </Menu>
            <Row gutter={8}>
                <Col xs={24} md={6}>
                    {isLoggedIn ? <UserProfile /> : <LoginForm />}
                </Col>
                <Col xs={24} md={12}>
                    {children}
                </Col>
                <Col xs={24} md={6}>
                    <a href="https://kjh950601.tistory.com/" target="_blank" rel="noreferrer noopener"></a>
                </Col>
            </Row>
        </div>
    )
}

AppLayout.propTypes = {
    children: PropTypes.node.isRequired,
};

export default AppLayout;

 

만약 styled-components를 사용하기 싫으면 useMemo를 사용하면 된다.

 

useCallback은 함수를 캐싱하는것이 useCallback이고 useMemo는 값을 캐싱하는 것이 useMemo이다. 아래와 같이 쓰면 리렌더링 되도 같은객체를 쓸 수 있다.(리렌더링 최적화)

import React, { useCallback, useState, useMemo } from 'react';
  const style = useMemo(() => ({ marginTop: 10}), []);
      <ButtonWrapper style={style}>
        <Button type="primary" htmlType="submit" loading={false}>로그인</Button>
        <Link href="/signup"><a>회원</a></Link>
      </ButtonWrapper>

 

리액트에서 함수형 컴포넌트에서 리랜더링 될때 LoginForm 함수안에 부분이 다시 실행된다.

useCallback은 캐싱이니까 이전컴포넌트랑 지금 컴포넌트랑 같은 걸로 친다.([] 배열 부분이 바뀌지 않는 이상)

 

 

useMemo도 마찬가지이다.

 

만약에 return 부분(Virtual DOM 부분)에 바뀐것이 있으면 실제화면에 바뀐부분만 다시 그린다.

리렌더링이 된다고 해서 return부분을 모두 다시그리는 것이 아니고 리턴 부분중에서도 바뀌는 부분만 다시 그린다.

리턴 부분 다시 그렸으면 얼마나 비효율적일까? 이 개념을 집고 넘어가자.

const LoginForm = () => {
// 리렌더링 시작
  const [id, setId] = useState('');
  const [password, setPassword] = useState('');

  const onChangeId = useCallback((e) => {
    setId(e.target.value);
  }, [])

  const onChangePassword = useCallback((e) => {
    setPassword(e.target.value);
  }, [])

  const style = useMemo(() => ({ marginTop: 10}), []);

  return (
  // 바뀐것이 있으면
    <Form>
      <div>
        <label htmlFor="user-id">아이디</label>
        <br />
        <Input name="user-id" value={id} onChange={onChangeId} required />
      </div>
      <div>
        <label htmlFor="user-password">비밀번호</label>
        <br />
        <Input 
          name="user-password"
          type="password"
          value={password}
          onChange={onChangePassword} 
          required 
        />
      </div>
      <ButtonWrapper style={style}>
        <Button type="primary" htmlType="submit" loading={false}>로그인</Button>
        <Link href="/signup"><a>회원</a></Link>
      </ButtonWrapper>
    </Form>
  );
}

즉 리액트에서 한번은 화면에 그려주고

그 다음에 리렌더링이 됐을 때 return 부분만 이전꺼와 지금꺼의 return 부분을 비교해서(즉 virtual dom끼리 비교)

어떤 부분이 새로운 객체 때문에 달라졌다 그러면 리액트가 다시 이부분만 그리는 것이다.

      <ButtonWrapper style={ marginTop: 10}>
        <Button type="primary" htmlType="submit" loading={false}>로그인</Button>
        <Link href="/signup"><a>회원</a></Link>
      </ButtonWrapper>

 

 

 

 

 

 

<출처 조현영: [리뉴얼] React로 NodeBird SNS 만들기>

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/dashboard

 

[리뉴얼] React로 NodeBird SNS 만들기 - 인프런 | 강의

리액트 & 넥스트 & 리덕스 & 리덕스사가 & 익스프레스 스택으로 트위터와 유사한 SNS 서비스를 만들어봅니다. 끝으로 검색엔진 최적화 후 AWS에 배포합니다., 새로 만나는 제로초의 리액트 노드버

www.inflearn.com

 

'React > NodeBird(ZeroCho)' 카테고리의 다른 글

noreferrer&noopener  (0) 2021.09.17
더미 데이터로 로그인  (0) 2021.09.17
로그인 폼 만들기  (0) 2021.09.17
반응형 그리드 사용  (0) 2021.09.16
_app.js & Head  (0) 2021.09.16