권재원
권재원 CTO

데이트팝 프론트엔드 개발자 기술면접

데이트팝 프론트엔드 개발자 기술면접

데이트팝 주니어 개발자 기술면접

이번 시간에는 React useState Hook을 직접 만들어가면서 Hook이 어떻게 동작하는지 확인해보는 시간을 가져보겠습니다.

React useState 흉내내기

React useState를 흉내내기 위해서 useState라는 함수를 직접 작성해보았습니다. 상태값 foo 와 상태값을 변경하는 setFoo 함수가 의도하는대로 동작하는지 설명해주세요. 그렇지 않다면 어떻게 코드를 수정하면 될지 제시해주세요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function useState(initialValue) {
  var _value = initialValue;

  function setState(newValue) {
    _value = newValue;
  }

  return [_value, setState];
}

const [foo, setFoo] = useState(0);
console.log(foo); // 여기에서는 초기값인 0이 출력됩니다.
setFoo(1);
console.log(foo); // 여기에서 상태값이 1로 변경이 될까요?

본격적으로 React useState 뿌시기

위에서 React useState 뼈대를 만들어보면서 공부해보았다면, 이번엔 실제 React 모듈을 만들어 React 내에서 useState가 어떻게 동작하는지 배워가보겠습니다.

DatepopReact라는 즉시 실행함수를 만들어 보았습니다. useStaterender가 있어 DatepopComponent를 실행 시켜주고 render 함수를 실행시키게 됩니다.

DatepopComponent에서는 DatepopReactuseState를 이용하여 counttext 상태값을 관리하도록 해보았습니다. click 함수와 type 함수를 실행 시킬 경우, 의도대로 상태값이 변경되는지 확인해보십다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
const DatepopReact = (function () {
  let _value;

  function useState(initialValue) {
    let state = _value || initialValue;

    const setState = function (newValue) {
      _value = newValue;
    };

    return [state, setState];
  }

  function render(Component) {
    const C = Component();
    C.render();
    return C;
  }

  return {useState, render};
})();

function DatepopComponent() {
  const [count, setCount] = DatepopReact.useState(0);
  const [text, setText] = DatepopReact.useState("popdeal");

  return {
    render: function () {
      console.log("render: ", {count, text});
    },
    click: function () {
      setCount(count + 1);
    },
    type: function (value) {
      setText(value);
    },
  };
}

let App = DatepopReact.render(DatepopComponent);
// 이 때, 출력으로 render: {count: 0, text: "popdeal"} 이 출력됩니다.
App.click();
App = DatepopReact.render(DatepopComponent);
// 이 때, 출력값으로 무엇이 나올까요?
App.type("poppass");
App = DatepopReact.render(DatepopComponent);
// 이 때, 출력값으로 무엇이 나올까요?

React useState 제대로 만들어보기

위의 문제에서 의도대로 상태값이 변경되지 않는다는 것을 알 수 있습니다. 이는 DatepopReactcounttext의 값을 독립적으로 관리하지 못하기 때문에 발생하는 문제입니다. 실제 React에서는 상태값을 컴포넌트 내에서 순서대로 관리합니다. 이를 위해 아래와 같이 코드를 수정하면 setState만 완성하면 됩니다. 이제 각 상태의 인덱스를 고정해 클로저 문제를 해결해보세요! 어떻게 완성시키면 될까요?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
const DatepopReact = (function () {
  let states = [];
  let index = 0;

  function useState(initialValue) {
    let state = states[index] || initialValue;

    const setState = function (newValue) {
      // 코드가 동작할 수 있도록 코드를 작성해주세요!
    };

    return [state, setState];
  }

  index += 1;

  function render(Component) {
    index = 0;
    const C = Component();
    C.render();
    return C;
  }

  return {useState, render};
})();

function DatepopComponent() {
  const [count, setCount] = DatepopReact.useState(0);
  const [text, setText] = DatepopReact.useState("popdeal");

  return {
    render: function () {
      console.log("render: ", {count, text});
    },
    click: function () {
      setCount(count + 1);
    },
    type: function (value) {
      setText(value);
    },
  };
}

let App = DatepopReact.render(DatepopComponent);
// 이 때, 출력으로 render: {count: 0, text: "popdeal"} 이 출력됩니다.
App.click();
App = DatepopReact.render(DatepopComponent);
// 이 때, 예상 출력값은 render: {count: 1, text: "popdeal"}
App.type("poppass");
App = DatepopReact.render(DatepopComponent);
// 이 때, 예상 출력값은 render: {count: 1, text: "poppass"}

React useState 완성하기

이제 위에서 React useState가 의도대로 동작하는 것을 알 수 있습니다. 하지만, 하나가 아쉽네요.

1
2
3
    click: function () {
      setCount(count + 1);
    },

click 함수를 보면 count 자기 자신을 하나 증가 시키는 구조로 되어 있는데 React에서는 상태값이 비동기로 변경되기 때문에 이전 상태값을 매개변수로 받아 변경하는 방식을 사용합니다.

1
2
3
    click: function (prev) {
      setCount(prev + 1);
    },

이런 식으로 변경이 가능합니다. 이렇게 되면 useState 도 같이 변경해야합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
const DatepopReact = (function () {
  let states = [];
  let index = 0;

  function useState(initialValue) {
    let state = states[index] || initialValue;
    const _index = index;

    const setState = function (newValue) {
      if (typeof newValue === "function") {
        states[_index] = newValue(...); // 이전 상태값으로 인자를 어떻게 넣어주면 될까요?
      } else {
        states[_index] = newValue;
      }
    };

    return [state, setState];
  }

  index += 1;

  function render(Component) {
    index = 0;
    const C = Component();
    C.render();
    return C;
  }

  return {useState, render};
})();

function DatepopComponent() {
  const [count, setCount] = DatepopReact.useState(0);
  const [text, setText] = DatepopReact.useState("popdeal");

  return {
    render: function () {
      console.log("render: ", {count, text});
    },
    click: function () {
      setCount(function (prev) {
        return prev + 1;
      });
    },
    type: function (value) {
      setText(value);
    },
  };
}

let App = DatepopReact.render(DatepopComponent);
// 이 때, 출력으로 render: {count: 0, text: "popdeal"} 이 출력됩니다.
App.click();
App = DatepopReact.render(DatepopComponent);
// 이 때, 예상 출력값은 render: {count: 1, text: "popdeal"}
App.type("poppass");
App = DatepopReact.render(DatepopComponent);
// 이 때, 예상 출력값은 render: {count: 1, text: "poppass"}

comments powered by Disqus