【问题标题】:How to map over a response from a REST call? [duplicate]如何映射来自 REST 调用的响应? [复制]
【发布时间】:2020-09-13 17:06:46
【问题描述】:

我有以下代码,我在其中进行 REST 调用并将结果分配给变量。

然后我使用结果来映射并使用道具创建组件。

但目前它会抛出错误,因为列表的值未定义。

我相信这是因为当我尝试映射时,由于 axios 异步调用尚未完成,列表的值尚未设置。

因此 2 个查询。

  1. 我应该如何使用响应值。我将其分配给变量“列表”的方法是正确的还是应该以不同的方式完成?

  2. 如何等待列表被填充然后映射它?

您可以通过查看以下端点来查看 response.data 的外观:https://sampledata.free.beeceptor.com/data1

响应数据示例:

[
    {
        "word": "Word of the Day",
        "benevolent": "be nev o lent",
        "adjective": "adjective",
        "quote": "well meaning and kindly.<br/>a benevolent smile",
        "learn": "LEARN MORE"
    },
    {
        "word": "Word of the Day",
        "benevolent": "be nev o lent",
        "adjective": "adjective",
        "quote": "well meaning and kindly.<br/>a benevolent smile",
        "learn": "LEARN MORE"
    }
]

客户端代码:

const App = () => {

  // const cardData = useSelector(state => state.cardData)
  let list;

  useEffect(() => {
    axios.get('https://sampledata.free.beeceptor.com/data1')
      .then(response => {
        list = response.data;
        list.forEach(l => console.log(l))
      })
      .catch(error => {
        console.log(error)
      })
  }, [])

  return (
    <>
      <ButtonAppBar/>
      <div className='container'>
        <div className='row'>
          {
            list.map((data) => {
              const {word, bene, adj, well, learn} = data;
              return (
                <div className='col-lg-3 col-md-6 format'>
                  <SimpleCard word={word} bene={bene} adj={adj} well={well} learn={learn} />
                </div>
              )
            })
          }
        </div>
      </div>
    </>
  );
}

export default App;

【问题讨论】:

  • 阅读useState。

标签: javascript reactjs axios


【解决方案1】:

您需要使用useState 来存储您从 API 获取的数据。

例如

const [state, setState] = useState({ list: [], error: undefined })

因为 API 调用是异步的,并且在组件第一次挂载之前数据将不可用。你需要使用条件来检查state.list.length,否则会抛出错误cannot read property ..x of undefined。

const App = () => {

  // create a state variable to store the data using useState
  const [state, setState] = useState({ list: [], error: undefined });

  useEffect(() => {
    axios
      .get("https://sampledata.free.beeceptor.com/data1")
      .then(response => {
        setState(prevState => ({
          ...prevState,
          list: [...prevState.list, ...response.data]
        }));
      })
      .catch(error => {
        setState(prevState => ({ ...prevState, list: [], error: error }));
      });
  }, []);

  return (
    <>
      <ButtonAppBar/>
      <div className='container'>
        {
           // you can show a loading indicator while your data loads
           !state.list.length && <div>The data is loading....</div>
        }

        <div className='row'>
          {
            state.list.length && state.list.map((data) => {
              const {word, bene, adj, well, learn} = data;
              return (
                <div className='col-lg-3 col-md-6 format'>
                  <SimpleCard word={word} bene={bene} adj={adj} well={well} learn={learn} />
                </div>
              )
            })
          }
        </div>
      </div>
    </>
  );
}

【讨论】:

  • 我已经删除了代码框示例,因为您提供的 API URL 的最大限制为每天 50 个请求。这是一个使用 jsonplaceholder API 的类似示例。 codesandbox
【解决方案2】:

您可以从这里使用useState 钩子中受益。

例如:

const App = () => {
  const [list, setList] = useState([]);

  useEffect(() => {
    axios.get('https://sampledata.free.beeceptor.com/data1')
      .then(response => {
        setList(response.data);
      })
      .catch(error => {
        console.log(error)
      })
  }, [])

  return (
    <>
      <ButtonAppBar/>
      <div className='container'>
        <div className='row'>
          {
            list.map((data) => {
              const {word, bene, adj, well, learn} = data;
              return (
                <div className='col-lg-3 col-md-6 format'>
                  <SimpleCard word={word} bene={bene} adj={adj} well={well} learn={learn} />
                </div>
              )
            })
          }
        </div>
      </div>
    </>
  );
}

export default App;

【讨论】:

    【解决方案3】:

    不要使用 let 来保存获取的值,而是使用 state 或 props,以防您想从中生成 UI。如果状态或道具值发生变化,则在反应组件中重新渲染。 出现错误的原因是,您正在进行异步调用,因此您的组件是并行渲染的,并且在 return list 内部将为 null 并且它会抛出错误。

    正确的方法是:

    const App = () => {
    
      const [list, setlist]= React.useState([])
    
    
      useEffect(() => {
        axios.get('https://sampledata.free.beeceptor.com/data1')
          .then(response => {
            setlist (response.data)
          })
          .catch(error => {
            console.log(error)
          })
      }, [])
    
      return (
        <>
          <ButtonAppBar/>
          <div className='container'>
            <div className='row'>
              {
                list.map((data) => {
                  const {word, bene, adj, well, learn} = data;
                  return (
                    <div className='col-lg-3 col-md-6 format'>
                      <SimpleCard word={word} bene={bene} adj={adj} well={well} learn={learn} />
                    </div>
                  )
                })
              }
            </div>
          </div>
        </>
      );
    }
    
    export default App;
    

    【讨论】:

      【解决方案4】:

      这可以通过两种方式解决(因为您使用的是钩子)

      1. useRef()(我不建议这样做)
      2. useState()(作为我给出的例子)

      我将使用 useState 方法向您展示,但您应该记住,由于它是一个状态,它将重新渲染(我认为这不会成为问题)。

      import React, { useEffect, useState } from 'react';
      import axios from 'axios';
      
      const App = () => {
        let [list, setList] = useState(<>LOADING</>);
        useEffect(() => {
            // You can use your link here
            // I have created corsenabled.herokuapp.com just to bypass the CORS issue. It's only for testing and educational purpose only. No intention to infringe any copyrights or other legal matters
            // I have used jsonplaceholder.typicode.com as an example
          axios.get('https://corsenabled.herokuapp.com/get?to=https://jsonplaceholder.typicode.com/posts')
            .then(response => {
              let tempData = response.data;
              let anotherData = tempData.map(data => {
                  return (<div>{data.userId}<br/>{data.id}<br/>{data.title}<br/>{data.body} <br/><br/></div>)
                  })
              // tempData = tempData.map(data => <div> {JSON.stringify(data)} </div>)
              console.log(tempData)
              setList(anotherData)
            })
            .catch(error => {
              console.log(error)
            })
        }, [])
        return (
          <>
            <div className='container'>
              <div className='row'>
                {
                  list
                }
              </div>
            </div>
          </>
        );
      }
      export default App;
      

      【讨论】:

      • 您的答案应该位于此站点上,而不是在站外链接中。链接可能会随着时间的推移而中断。
      • 这是一个 github gist 链接,顺便说一句,谢谢你会编辑它
      • 我很抱歉,我认为您可能已经否决了它,因为我花了很长时间才更改它。我删除了我的评论,再次对此感到抱歉。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-27
      • 1970-01-01
      • 2018-11-25
      • 1970-01-01
      • 1970-01-01
      • 2018-09-11
      • 1970-01-01
      相关资源
      最近更新 更多