React 오프라인 6강 - useState

어쩌다·2022년 7월 7일
0

React 오프라인 6강 - useState


함수를 만들어 보자

function App() {
  	let number = 1;
  const add = () => {
    number++;
    console.log("add", number);
  }
  
  return (<div>
          	<h1>숫자 : {number}</h1>
            <button>더하기</button>
          </div>)
}
  1. JS에서 함수는 1급 객체이기 때문에 함수 안에 함수를 만들어도 된다.
  2. 이렇게 1씩 ++이 되는 함수를 만들고 return문에 number를 출력하도록 했다.
  3. 그리고 더하기 버튼을 통해 버튼을 누를 때마다 number라는 상태값을 변하게 할 것이다.
function App() {
  	let number = 1;
  const add = () => {
    number++;
    console.log("add", number);
  }
  
  return (<div>
          	<h1>숫자 : {number}</h1>
            <button onClick={add}>더하기</button>
          </div>)
}
  1. 이렇게 onClick이라는 함수 안에 직접 만든 함수를 바인딩할 것이다.
  2. 여기에서 stack, 매개변수 자리가 되는 ()를 적으면 안 된다.
  3. 바인딩이 아닌 버튼 태그가 그려질 때 함수가 바로 실행이 되기 때문이다.
  4. 이렇게 만든 후 버튼을 눌러봐도 화면은 바뀌지 않고 콘솔에만 번호가 증가된다.
  5. number가 만약에 상태 변수라면 어떨까?
  6. React는 상태(데이터)가 변하는 것을 감지하여 렌더링하는 엔진이다. 이 엔진이 상태를 인지할 수 있도록 선언해줘야 한다.

useState 선언하기

function App() {
  const [number, setNumber] = useState(1);
  // React 안에 있는 hooks 라이브러리
  
  const add = () => {
    number++;
    console.log("add", number);
  }
  
  return (<div>
          	<h1>숫자 : {number}</h1>
            <button onClick={add}>더하기</button>
          </div>)
}
  1. 이렇게 선언해주면 setNumber를 통해 상태값을 변경해야 한다.
function App() {
  const [number, setNumber] = useState(1);
  // React 안에 있는 hooks 라이브러리
  
  const add = () => {
    setNumber(number + 1);
    console.log("add", number);
  }
  
  return (<div>
          	<h1>숫자 : {number}</h1>
            <button onClick={add}>더하기</button>
          </div>)
}
  1. 이는 실제로 number값을 바꾸는 게 아니라 React한테 값을 변경하라고 시키는 것이다.
  2. number++를 하는 것은 의미가 없다. const는 변경할 수 없으니까.
  3. 또한 더하기 버튼을 누를 때마다 add를 통해 상태값이 변경되고 re-rendering을 하게 되면서 return값은 모두 새로 그려진다.
  4. 하지만 이를 부분값만 변경하는 방법도 있다.
const Sub = () => {
  return <div>
  	<h1>Sub입니다.</h1>
  </div>;
}
  1. 이렇게 컴포넌트를 만들고
function App() {
  const [number, setNumber] = useState(1);
  // React 안에 있는 hooks 라이브러리
  
  const add = () => {
    setNumber(number + 1);
    console.log("add", number);
  }
  
  return (<div>
          	<h1>숫자 : {number}</h1>
            <button onClick={add}>더하기</button>
            <Sub />
          </div>)
}
  1. 이렇게 넣어버리면 된다.
  2. 따라서 App.js가 부모이고 Sub.js가 자식이라고 할 수 있다.
  3. Sub은 데이터가 없는 거라면 한 번만 그려져도 될 것이다.
  4. 이는 함수를 통해서 렌더링을 하지 않도록 막을 수 있다.
  5. 자식이 꼬리를 꼬리로 물게 되면 퍼포먼스가 좋지 않기 때문에 컴포넌트를 통해서 관리하는 게 좋다.

example

function App() {
  const [users, setUsers] = useState([]);
  // React 안에 있는 hooks 라이브러리
  // 다운로드를 받았다고 하자.
  
  const download = () => {
    let sample = [
      {
        id: 1, name: "홍길동",
      },
        {
        id: 2, name: "임꺽정",
      },
        {
        id: 3, name: "매즈 미켈슨",
      },
        {
        id: 4, name: "세종대왕",
      },
    ];
    setUsers([...users, ...sample]);
  }
  
  return (<div>
            <button onClick={download}>다운로드</button>
          </div>)
}
  1. 전개 연산자를 사용하여 다운로드 받은 users와 sample을 나열했다.
setUsers([...sample]);
  1. 이렇게 되면 users에 다운로드 받은 데이터가 있다고 해도 sample로 덮어씌워지게 된다.
const [users, setUsers] = useState([{id: 5, name: "하늘"}]);
setUsers([...users, ...sample]);
  1. 이렇게 된다면 sample의 다음 데이터에 users에 담긴 데이터도 함께 나열될 것이다.
function App() {
  const [users, setUsers] = useState([]);
  // React 안에 있는 hooks 라이브러리
  // 다운로드를 받았다고 하자.
  
  const download = () => {
    let sample = [
      {
        id: 1, name: "홍길동",
      },
        {
        id: 2, name: "임꺽정",
      },
        {
        id: 3, name: "매즈 미켈슨",
      },
        {
        id: 4, name: "세종대왕",
      },
    ];
    setUsers([sample]);
  }
  
  return (<div>
            <button onClick={download}>다운로드</button>
            {users.map(u => <h1>{u.id}, {u.name}</h1>)}
          </div>)
}
  1. 이를 전개연산자를 사용하지 않고 데이터를 렌더링해보자.
  2. 그러면 다운로드 함수를 통해 users라는 상태값이 변경된 것을 감지하고 배열에 있는 데이터가 re-rendering 된다.
  3. 이미 users에 같은 데이터가 있었다면 re-rendering이 될까?
function App() {
  console.log('APP 실행됨');
  const [users, setUsers] = useState([ {
        id: 1, name: "홍길동",
      },
        {
        id: 2, name: "임꺽정",
      },
        {
        id: 3, name: "매즈 미켈슨",
      },
        {
        id: 4, name: "세종대왕",
      },]);
  // React 안에 있는 hooks 라이브러리
  // 다운로드를 받았다고 하자.
  
  const download = () => {
    let sample = [
      {
        id: 1, name: "홍길동",
      },
        {
        id: 2, name: "임꺽정",
      },
        {
        id: 3, name: "매즈 미켈슨",
      },
        {
        id: 4, name: "세종대왕",
      },
    ];
    setUsers([sample]);
  }
  
  return (<div>
            <button onClick={download}>다운로드</button>
            {users.map((u) => <h1>{u.id}, {u.name}</h1>)}
          </div>)
}
  1. 이처럼 초기에 같은 데이터를 가지고 있고, 다운로드 함수를 실행시킨다고 해서 re-rendering이 될지 콘솔로 찍어보자.
  2. 결국 똑같은 데이터지만, 레퍼런스가 다른 것(sample)이 들어갔다.
  3. 따라서 레퍼런스가 다르면 데이터가 다르다고 변경 감지를 하기 때문에 re-rendering이 된다.
function App() {
      let sample = [
      {
        id: 1, name: "홍길동",
      },
        {
        id: 2, name: "임꺽정",
      },
        {
        id: 3, name: "매즈 미켈슨",
      },
        {
        id: 4, name: "세종대왕",
      },
    ];
  console.log('APP 실행됨');
  const [users, setUsers] = useState(sample);
  // React 안에 있는 hooks 라이브러리
  // 다운로드를 받았다고 하자.
  
  const download = () => {

    setUsers(sample);
  }
  
  return (<div>
            <button onClick={download}>다운로드</button>
            {users.map((u) => <h1>{u.id}, {u.name}</h1>)}
          </div>)
}
  1. 이렇게 되면 어떨까? 똑같은 데이터의 레퍼런스를 사용하고 공유하기 때문에 데이터는 똑같다고 인식할 것이다.
  2. 결국은 상태값은 레퍼런스가 변경되어야 동작하게 된다.
  3. 그렇기 때문에 '깊은 복사'를 해야 한다. 깊은 복사는 새로운 레퍼런스에 데이터를 복사하기 때문이다.
function App() {
      let sample = [
      {
        id: 1, name: "홍길동",
      },
        {
        id: 2, name: "임꺽정",
      },
        {
        id: 3, name: "매즈 미켈슨",
      },
        {
        id: 4, name: "세종대왕",
      },
        
    ];
  console.log('APP 실행됨');
  const [users, setUsers] = useState(sample);
  // React 안에 있는 hooks 라이브러리
  // 다운로드를 받았다고 하자.
  
  const download = () => {
		sample.push({
        id: 5, name: "까마귀",
      },); // 기존 데이터를 변경함
    console.log(sample); // 데이터가 변경되었는 지 확인
    setUsers(sample);
    console.log(sample);
  }
  
  return (<div>
            <button onClick={download}>다운로드</button>
            {users.map((u) => <h1>{u.id}, {u.name}</h1>)}
          </div>)
}
  1. 콘솔을 보면 sample 데이터가 바뀌었다는 것을 알 수 있다.
  2. 하지만 기존 데이터를 변경했고 레퍼런스는 바뀌지 않았기 때문에 렌더링은 다시 되지 않는다.
  3. 따라서 '깊은 복사'를 해야 상태 데이터를 변경할 수 있다는 것이다.
function App() {
      let sample = [
      {
        id: 1, name: "홍길동",
      },
        {
        id: 2, name: "임꺽정",
      },
        {
        id: 3, name: "매즈 미켈슨",
      },
        {
        id: 4, name: "세종대왕",
      },
        
    ];
  console.log('APP 실행됨');
  const [users, setUsers] = useState(sample);
  // React 안에 있는 hooks 라이브러리
  // 다운로드를 받았다고 하자.
  
  const download = () => {
		const a = sample.concat({
        id: 5, name: "까마귀",
      },); // 기존 데이터를 변경함
    setUsers(a);
  }
  
  return (<div>
            <button onClick={download}>다운로드</button>
            {users.map((u) => <h1>{u.id}, {u.name}</h1>)}
          </div>)
}
  1. 이렇게 되면 데이터가 새로운 레퍼런스로 깊은 복사를 했기 때문에 렌더링이 처음엔 되지만, sample은 불변이기 때문에 기존 데이터가 변경되지 않는 이상 추가한 데이터는 계속해서 렌더링되지 않을 것이다.
  2. 처음에 다운로드 버튼을 눌러 까마귀가 추가될 뿐이지, 이후로는 같은 데이터이기 때문에 까마귀만 렌더링될 뿐이다.
    setUsers([...sample, {
        id: 5, name: "까마귀",
      }]);
  1. 그러면 기존 데이터를 해치지 않고 새롭게 다운로드 받은 데이터를 넣을 때는 전개 연산자를 사용하면 된다.

출처

React 오프라인 6강 - useState - 메타코딩

profile
혼자 공부하는 공간

0개의 댓글