[React] useState 값이 느리게 들어올 땐 useEffect

·2023년 11월 18일
0

React

목록 보기
4/8
post-thumbnail

문제 1 (다른 값 들어오는 속도보다 느릴 때)


유튜브 영상을 끌어와서 게시글 업로드하는 기능 구현 중!
유튜브 링크를 복사+붙여넣기 하여 가져오면 그 중에서 해당 콘텐츠의 id, title, thumnail 정보를 가져오려고 한다. 그리고 다음 페이지로 이동시 그 값을 전달할 것이다.

  const [text, setText] = useState('');
  const [videoId, setVideoId] = useState('');
  const [formValue, setFormValue] = useState({
    id: '',
    thumnail: '',
    title: '',
  });

...

// 인풋창에 유튜브 링크 입력하면 id 가져오기
  const changeHandler = (e) => {
    setText(e.target.value);
    getYoutubeID(e.target.value);
  };

// 유튜브 링크에서 id 가져오기
  const getYoutubeID = (url) => {
    let regExp =
      /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/;
    if (url) {
      let matchs = url.match(regExp);
      console.log(matchs[7]);
      return setVideoId(matchs[7]);
    }
  };

// id, thumnail, title 값을 가져오는 기능
// 유튜브 api key는 따로 발급 받아야함
  const getVideoInfo = async (videoId) => {
    const url = `https://www.googleapis.com/youtube/v3/videos?id=${videoId}&key=${API_KEY}&part=snippet,contentDetails,statistics,status`;
    const options = {
      method: 'GET',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json;charset=UTF-8',
      },
    };
    try {
      let res = await fetch(url, options);
      let resOk = res && res.ok;
      if (resOk) {
        const resData = await res.json();
        const formValue = {
          id: resData.items[0].id,
          title: resData.items[0].snippet.channelTitle,
          thumnail: resData.items[0].snippet.thumbnails.default.url,
        };
        console.log(formValue);
        return setFormValue(formValue);
      }
    } catch (err) {
      console.log(err);
    }
  };

// 링크 모양 버튼을 누르면 값을 얻어와서 다음 페이지로 전해주는 기능
// navigate('/', { state: { key:value, key:value } } 코드 작성시 state 값을 해당 페이지로 전달할 수 있다.
  const uploadHandler = () => {
    if (videoId) {
      getVideoInfo(videoId);
      console.log(formValue);
		navigate('/upload/uploadpost', { state: { formValue } });
    } else {
      alert('올바른 유투브 링크를 입력해주세요.');
    }
  };

...

<form>
<Input
	name='inputYoutube'
	type='text'
	id='youtube'
	onChange={changeHandler}
	defaultValue={text}
/>
<BtnUpload onClick={uploadHandler} type='button' />
</form>

이렇게 코드를 작성하였는데, 비동기 처리를 하다보니 영상의 정보 값이 아직 들어오기도 전에 네비게이터가 먼저 작동해 다음 페이지로 이동해버리는 문제가 있었다.

이 문제를 해결하기 위해 useEffect 사용!
컴포넌트가 렌더링될 때마다 특정 작업을 수행한다.
useEffect(()=>{수행하고자 하는 작업, [검사하고자 하는 값]})

  const [state, setState] = useState(0);

...

  useEffect(() => {
    setState(state + 1);
    if (state === 1) {
      console.log(state);
      console.log(formValue);
      navigate('/upload/uploadpost', { state: { formValue } });
    }
    //eslint-disable-next-line
  }, [formValue]);

state에 0 상태를 주고, formValue 값을 검사해 복사+붙여넣기를 통해 값이 한번 바뀐 후(들어온 후)에 페이지를 이동하여 값이 전달될 수 있도록 바꾸었다.

문제 2 (값이 한박자 늦게 찍힐 때)

이번엔 콘텐츠의 value 값들을 useState를 이용해 받아오고 있었는데 한박자씩 늦게 찍히는 문제가 발생했다.

  const [content, setContent] = useState({
    id: id,
    title: title,
    text: '',
    thumnail: thumnail,
    visibility: 'public',
  });

...

// 내용이 변할때 그 값을 state에 업데이트 해주는 기능
  const getValueHandler = (e) => {
    const { name, value } = e.target;
    setContent({ ...content, [name]: value });
    console.log(content);
  };

  const visibilityHandler = (e) => {
    console.log(e.target.name, e.target.value);
    setContent({ ...content, [e.target.name]: e.target.value });
    console.log(content);
  };

...

// 핸들러 함수 적용
<TextArea name='text' type='text' onChange={getValueHandler} />

<SelectVisibility
	name='visibility'
	size='1'
	defalutValue='public'
	onChange={visibilityHandler}>
<option value='public'>공개</option>
<option value='private'>비공개</option>
</SelectVisibility>

특히 select에서 공개 여부를 컨트롤해야 했는데, 공개를 선택하면 이전 상태인 비공개가 값으로 들어오고 비공개를 선택하면 이전 값인 공개가 값으로 들어오는 문제가 있었다.

콘솔로그를 찍어보니 상태는 private인데도 값은 public으로... public을 선택하면 이전 값인 private으로 들어오고 있는 걸 확인할 수 있었다.
텍스트 값도 한박자씩 늦게 들어오고 있었다. 처음으로 1을 눌렀을 때 텍스트 값은 '', 2를 눌렀을 때 비로소 값이 '1'이 들어온다.
(비슷한 다른 예시 : 비밀번호 확인을 위해 두 state를 비교 할 때 값은 같은데 틀리다고 나오는 경우)

문제 해결을 위해 이번에도 useEffect를 사용하기로 했다.

  useEffect(() => {
    console.log(content);
    setContent(content);
    //eslint-disable-next-line
  }, [content.text, content.visibility]);

useEffect를 사용해 text와 visibility의 상태가 바뀔 때마다 그 값을 content에 넣기로 했다! 이제 상태와 값이 일치되었다.

둘 다 적절한 방법인지 모르겠지만, useState 사용시 값이 느리게 들어오는 문제를 useEffect를 사용해 해결해보았다!

profile
주니어 프론트엔드 웹 개발자 🐛

0개의 댓글