seminar-2020 icon indicating copy to clipboard operation
seminar-2020 copied to clipboard

예제로 올려주신 context-tutorial project에 관해

Open woohm402 opened this issue 4 years ago • 7 comments

올려주신 코드를 실행했을 때, toggle이 정상 작동하지 않고(한번 켜지면 다시 안 꺼짐), add item을 했을 때 아이템이 최대 한 개만 추가되며, 아이템이 추가된 상태에서 toggle을 했을 때는 추가된 아이템이 사라집니다. 아마도 정상 작동하는 상태인 건 아닌 것 같은데, 의도하신 건지, 아니면 버그인 건지 궁금합니다!

레포 clone한 다음 yarn 하고 yarn start하는 방법으로 실행해 보았습니다! 브라우저는 구글 크롬 이용하였습니다.

woohm402 avatar Oct 03 '20 16:10 woohm402

앗 실수로 라벨을 안 달았네요..ㅠㅠ 아마 권한도 없는 것 같은데 수정을 어떻게 하는지 모르겠습니다(Frontend HW3)ㅠㅠ 죄송합니다

woohm402 avatar Oct 03 '20 16:10 woohm402

@woohm402 권한 초대 다시 드리겠습니다 :)

davin111 avatar Oct 03 '20 19:10 davin111

저도 원래 의도하신 작동은 add Item을 여러 번하면 item들이 계속 추가되고, toggle 버튼을 누르면 같은 name을 가진 요소들이 배경색이 on/off 되는 방식이었다고 생각했습니다. (만약 아니거나 제가 놓친 부분 있다면 다시 정정 부탁드립니다!)

올려주신 코드가 위의 방식대로 작동하지 않는 이유는 setState() 안에서 state가 비동기로 update되기 때문에 이전 state를 제대로 반영하지 못해서인걸로 알고 있습니다. 그래서 다음 state를 이전 state로부터 직접적으로 계산하면 안 되고, function을 전달받는(이전 state를 받아 다음 state를 반환하는 function) setState()의 두 번째 form을 사용해야 하는 것 같습니다. 해당 내용이 설명되어 있는 React Doc 페이지입니다.

그래서 저의 경우에는 setState()하는 부분의 코드

const addItem = ({ name, seminar }) => {
    setState({
      ...state,
      list: [
        ...state.list,
        { name, seminar, on: false }
      ]
    })
  }

  const onToggle = (name) => {
    setState({
        ...state,
        list: state.list.map(item => {
          return item.name === name ?
          { ...item, on: !item.on } :
          { ...item }
        })
      }
    )
  }

setState((prevState) => {})의 형식을 이용하여 아래와 같이 고쳤더니

  const addItem = ({ name, seminar }) => {
    setState((state) => ({
      ...state,
      list: [...state.list, { name, seminar, on: false }],
    }));
  };

  const onToggle = (name) => {
    setState((state) => ({
      ...state,
      list: state.list.map((item) => {
        return item.name === name ? { ...item, on: !item.on } : { ...item };
      }),
    }));
  };

잘 작동하더라고요. 저도 고민했던 부분이라 참고가 되셨으면 좋겠습니다! 혹시 제가 놓친 부분이나 잘못 이해한 부분 있다면 지적 부탁드립니다!

jusjinuk avatar Oct 03 '20 21:10 jusjinuk

아하.. 비동기일 수 있어서 발생하는 문제였군요ㅠㅠ 저도 잘 작동하네요 감사합니다!!

woohm402 avatar Oct 04 '20 01:10 woohm402

약간 피곤한 상태에서 작성하다보니 실수가 있었습니다 ㅠㅠ @jusjinuk 께서 해결하는 법은 정확히 알려주셨네요 위 방법대로 수정하면 잘 작동합니다!


useState 를 이용해서 contextvaluestate로 만들고 이를 관리합니다. 이 value에는 전역으로 관리하는 배열 뿐만 아니라, 이에 변화를 줄 함수 또한 포함합니다. 예시 코드를 보시면 setState배열에만 액션을 가합니다. 따라서, 액션 함수를 호출할 때마다 배열은 값이 변하게 됩니다. 하지만, 두 개의 액션 함수useStatestate 를 선안한 시점부터 사이트가 꺼질 때까지 동일한 함수를 가지고 있습니다. 문제는 이 액션 함수 자체에 있습니다. 액션 함수에는 아래와 같이 state 를 참조하는 부분이 있습니다.

setState({
  ...state,
  list: state.list.map(() => {})
})

문제는 이 state액션 함수선언되는 시점을 기준으로 합니다. 따라서, 이 stateuseState 에 인자로 전달된 값과 동일합니다. 이 함수를 아무리 호출한다 할지라도 초기값을 기준으로 배열이 변합니다. 따라서, 아무리 추가해도 하나만 추가되고, toggle도 하나만 작동합니다.

이를 해결하기 위한 방법은 위해서 정확하게 말씀해주셨는데요 setState((prev) => {}) 와 같은 꼴로 setState 를 작성하시면 됩니다. 이 때 콜백 함수로 전달된 prev 에는 최신의 state 값이 자동으로 전해지고 이를 사용하시면 정상 작동할 겁니다.

@jusjinuk 께서 올려주신 링크는 Class ComponentsetState 에 대해서 얘기하고 있는 것 같아요. 지금 세미나에서 사용 중인 건 react hooksuseState 라 해당 링크와 약간의 차이는 있을 수 있습니다. 예를 들면, react hookssetState 함수는 두 번째 파라미터를 전달받지 않습니다 ㅎㅎ

ars-ki-00 avatar Oct 05 '20 13:10 ars-ki-00

정확히는 그런 이유였군요 감사합니다!

woohm402 avatar Oct 05 '20 14:10 woohm402

아 그런 것이군요! 지난주에 이미 올라와 있었네요.... ㅎㅎ 감사합니다

Wits15730 avatar Oct 11 '20 11:10 Wits15730