망냥
51
2020-05-11 19:44:41
15
904

리액트 렌더링 질문드립니다


안녕하세요. 며칠 째 해결하지 못해 질문드립니다 ㅠ 일단 저는 리액트 초보인데요,

API 서버에서 클릭시 입력한 쿼리와 주소를 합해서 데이터를 받아오고 보여주는 것을 하고 싶은데요,

일단 받아오는 것은 되나, 순서가 이상합니다... 


예를 들어 china 입력->클릭 시, 바로 china의 데이터가 페이지에 띄워져야하는데 바로 띄워지지않고, Korea를 입력 후 클릭시 -> china의 데이터가 띄워집니다. 그리고 다시 Japan을 입력하고 클릭해야 china의 데이터가 띄워집니다 ㅠ... 구글링해서 많은 글들을 보고 따라서 해봤지만 똑같이 한 번 더 클릭하고 렌더링을 하고나서야 그 이전에 원했던 데이터가 띄워지는데 뭐가 문제일까요? axios도 써보고 fetch도 써보고 useEffect의 두번째 인자값을 다 바꿔보고 해도 안되는데 이 컴포넌트의 문제가 아닌걸까요?

알려주시면 감사하겠습니다 !!


import React, {useStateuseEffectfrom 'react';
import axios from 'axios';

const Search = () => {
    const [datas,setDatas] = useState([]);  //[]만 되는 듯?
    const [query,setQuery] = useState('None');
    const [urlsetUrl] = useState(
        'http://localhost:8080/api/home/'
    )
    const [ex,setEx] = useState('None');

    useEffect(()=> {
        const fetchData = async ()=> {
            const result = await axios(`http://localhost:8080/api/home/${query}`);
            setDatas(result.data);
        };

        fetchData();
        const items = [];
        datas.map((item,index)=>{
            items.push(item);
            console.log("item : "+items[index].country_name);
        });

    },[url]);


    return(
        <div>
            <input 
                type="text"
                value={query}
                onChange={e=>setQuery(e.target.value)}
            />
           <button onClick={()=>setUrl(`http://localhost:8080/api/home/${query}`)}>click</button>

        </div>
    );
}

export default Search;


0
  • 답변 15

  • onizsmx
    200
    2020-05-11 20:44:25 작성 2020-05-11 20:52:57 수정됨

    일단 어디서부터 손을 대야할지 몰라서 최대한 비슷하게 유지하면서 바꿔봤어요..

    https://gist.github.com/onizsmx/d83c7ed70d56f75f1dc45c87b03586d3


    문제 1. useEffect 안에 items가 component state가 아니에요.

    문제 2. input에 onChange 안에서 setQuery를 통해 query state가 update 되는데 이렇게 되면 불필요한 state update가 많아져요.

    문제 3. 전반적으로 constant한 value는 한곳에 모으는게 좋아요. http://localhost ~~ 같은.

    문제 4. 지금은 await fetchData()로 하지 않아서 괜찮은데, ui에서 await은 바로 사용하지는 않아요. 잘못하면 UI 사용이 전부 멈춰요. react saga나 react thunk같은거 나중에 좀더 배우시고 사용하시면 좋아요.


  • 페코옹
    2k
    2020-05-11 21:37:13 작성 2020-05-11 22:36:05 수정됨


    import React, { useState } from 'react';
    import axios from 'axios';
    
    const URL = 'http://localhost:8080/api/home';
    
    function Search() {
      const [datas, setDatas] = useState([]);
      const [query, setQuery] = useState('');
    
      const handleChange = e => setQuery(e.target.value);
    
      const handleClick = () => {
        if (!query) return;
        axios
          .get(`${URL}/${query}`)
          .then(res => setDatas(res.data))
          .catch(err => console.log(err));
      };
    
      return (
        <div>
          <input type="text" value={query} onChange={handleChange} />
          <button onClick={handleClick}>click</button>
          {datas.map(data => (
            <div key={data.Country_name}>{data.Country_name}</div>
          ))}
        </div>
      );
    }
    
    export default Search;
    

    이렇게 해보세요


  • 망냥
    51
    2020-05-11 21:42:32

    Frudy


    아 자세히 답변해주셔서 감사합니다! item은 setData가 계속 안되길래 어떻게 찍히나 보고싶어서 넣은거였어요 ㅠ 뭔가 순서가 잘못되었다는 느낌은 계속 들었는데...  여전히 콘솔에는 다른 인풋을 입력하고 클릭하고 나서야 콘솔에 그 이전의 데이터가 입력되는데 뭐가 문제일까요...?




  • 망냥
    51
    2020-05-11 21:52:33

    onizsmx


    다들 친절한 답변 너무 감사합니다.

     .then(res => {
            if (res.data && res.data.length) {
              let newItems = [];
              res.data.map(d => {
                  newItems.push(d)
                  console.log(newItems);
                });
              setItems(newItems);
              console.log('New items:'items);
            }
          })


    주신 코드에서 제가 겪었던 문제 그대로 첫 클릭에선 New items : undefined로 뜨고 다시 다른 인풋을 입력 후 클릭해야만 New items : China... 이런 식으로 뜨네요 res.data.map 안의 콘솔로그만 바로 찍히는데 

    제가 그냥 전체 구조를 아예 잘못 이해하고 있는걸까요?

    <div>
              {query !== 'None' ?
                items.map(item => {
                    <p>{item}</p>
                }) : null
            }
          </div>

    이런 식으로 클릭 시 바로 웹에 아이템을 띄우고 싶었었는데 계속 막히네요... 도움 정말 감사합니다...

    리액트 강의를 처음부터 다시 보고 와야되는 것 같네요 ㅠ

  • Frudy
    7k
    2020-05-11 21:53:12

    아~ 

            const items = [];
            datas.map((item,index)=>{
                items.push(item);
                console.log("item : "+items[index].country_name);
            });


    이 코드가 단순히 확인하려고 하셨던 코드셨군요.

    이 컴포넌트와 관련된 다른 컴포넌트 소스코드도 같이 올려주셨으면 좋겠어요.


    그리고 원하시는게 어떤거에요?

    South Korea를 입력하고 클릭했을 때 콘솔에 South Korea가 뜨는걸 원하시는거에요?

    아니면.. setData가 되는걸 원하시는거에요?


    일단.. 현재 저 코드 상태에서는 콘솔에 무조건 이전값이 나올 수밖에 없다고 생각해요.

    아까 말씀드린대로, fetchData()가 실행된다음 데이터 받기도 전에 이미

    다음 코드가 실행되서 items.push가 진행되기 때문이에요.


    data를 정상적으로 받아오는지 확인하시려면...

    fetchData()에서 setData()할 때 data값을 콘솔로 찍어보시거나,

    네트워크 패널로 서버로부터 정상적인 값을 받았는지 확인해보시는게 좋을거같아요.

  • 망냥
    51
    2020-05-11 21:58:25

    페코옹

    도움 감사합니다 ㅠ 테스트 해봤는데 인풋 입력 시에 이런 에러가 뜨네요. datas를 못받은 거 같은데 뭐가 뭔지 답답하네요 흑흑 ㅠ 구글링해서 나오는 코드들은 간단하게 다들 실행되던데 뭐가 문제인건지... 그냥 처음부터 다시 찾아봐야겠네요 도움 감사합니다!!

    Error: Objects are not valid as a React child (found: object with keys {id, country_name}). If you meant to render a collection of children, use an array instead.




  • Frudy
    7k
    2020-05-11 22:02:22

    혹시 괜찮으시면 전체 소스코드 올려주시거나 git이면 링크를 부탁드려도될까요?

    저도 저 에러메시지는 처음보는거라서 저한테도 디버깅공부에 도움이 될거같아서요.

  • onizsmx
    200
    2020-05-11 22:05:04 작성 2020-05-11 22:06:53 수정됨

    map 할 떄 element 가 data object로 되서 그래요

    첫 클릭에 undefined 되는 이유는 query 'None' 으로 request 보냈을 때 response가 없을 거에요.

  • 망냥
    51
    2020-05-11 22:05:41

    Frudy

    South Korea를 입력 클릭시 setData로 data에 저장이 되고, 웹에 data를 바로 띄워주는 것이 목표입니다.


    import axios from 'axios';
    import React, { useStateuseEffect } from 'react';
        const [datas,setDatas] = useState([]);  //[]만 되는 듯?
        const [query,setQuery] = useState('None');
        const [urlsetUrl] = useState(
            'http://localhost:8080/api/home/'
        )

        useEffect(()=> {
            const fetchData = async ()=> {
                const result = await axios(`http://localhost:8080/api/home/${query}`);
                setDatas(result.data);
            };

            fetchData();
        

        },[url]);


        return(
            <div>
                <input 
                    type="text"
                    value={query}
                    onChange={e=>setQuery(e.target.value)}
                />
               <button onClick={()=>setUrl(`http://localhost:8080/api/home/${query}`)}>click</button>
                <div>
                    {query!=='None'?
                        datas.map(item => {
                            <p>{item.Country_name}</p>
                        }) : null
                    }
                </div>
            </div>
        );
    }

    export default Search;


    아래 div에 item.Country_name, 다른 정보... 이런 식으로요 ㅠ 네트워크란을 보면 서버에선 데이터를 정상적으로 받아오는게 확인되는데 setData가 안돼서 웹에 띄워지지 않는 것 같아 질문 드린거였습니다! 어떻게 해야 띄워질 수 있을까 해서 콘솔로그로 그냥 여기저기 넣어서 확인해본 거였구요 ㅠ 

  • onizsmx
    200
    2020-05-11 22:11:07 작성 2020-05-11 22:12:52 수정됨

    datas.map(item=>{})

    여기서

    <p> 리턴해야해요 둘중 하나에요

    datas.map(item=>{return<p>{item.Country_name}</p>})

    아니면

    datas.map(item=><p>{item.Country_name}</p>)

    둘다 같이 동작해요.

  • 페코옹
    2k
    2020-05-11 22:35:17

    렌더링 할 때 하기 처럼 변경해보세요

    데이터가 string이 아니라 object로 들어있어서 그래요

    <div key={data.country_name}>{data.country_name}</div>


    Error: Objects are not valid as a React child (found: object with keys {id, country_name}). If you meant to render a collection of children, use an array instead.


  • IT공부중
    266
    2020-05-11 22:36:40

    fetchData라는 함수를 밖으로 빼는게 좋아보이구요.

    그리고 위에분 말처럼 map은 반환값이 있어야하는데 {} 는 반환하는게 아니라 그래요~

    {query!=='None'?  datas.map(item => <p>{item.Country_name}</p> : null }

    이게 맞는거 같구요 arrow function은 => 랑 같은 줄에 하거나 ()로 감싸고 바로 값을 넣어주면 바로 return이 됩니다.
    {} 로 감싸고 로직 같은걸 적으면 return을 명시 해줘야해요!

  • 망냥
    51
    2020-05-11 22:45:44

    헉 ㅠㅠㅠㅠㅠ 너무 감사합니다  드디어 해결했네요 ㅠㅠ.... 

    제가 문법 이해가 없다보니 처음부터 문제가 어디였는지 모르고 질문 자체를 잘못 드렸던 것 같아요!  답변주신 분들 너무너무 감사합니다... 

    답답해서 던져버리고 싶었는데 해결되니까 넘 기쁘네요ㅠ 정말 감사합니다 

  • Frudy
    7k
    2020-05-11 22:51:20

    음... 전 여전히 의문이 있는대요,

    return을 안했는데

    어떻게 이전값이 화면에 보일수있을까요?


    map에서 return안하면 

    렌더링도 안될탠데~~~

    이전이던 현재던 간에 아예 안보여야하는게 아닐까요?


    제가 놓친부분이 어디인지 모르겠어요..

  • Frudy
    7k
    2020-05-11 22:58:45

    그리고 질문자님덕분에 저도 잘배웠어요 감사합니다.


    1. React Child에 {id: ..., country_name}같은 키를 넣어서 줘보니 저런 에러가 정말로 뜨네요.

    처음보는 에러배웠습니다.


    2. 전체가 아닌 일부의 소스코드만 봤음에도 합리적이지못하게 추론을 했었네요 제가...


    덕분에 잘배웠습니다 ~~

  • 로그인을 하시면 답변을 등록할 수 있습니다.