【问题标题】:Can't concatenate json server data into array using useEffect hook in React无法使用 React 中的 useEffect 挂钩将 json 服务器数据连接到数组中
【发布时间】:2020-07-04 10:55:17
【问题描述】:

我是 React 和全栈开发的新手,所以请多多包涵。我正在尝试将来自 json-server(我使用 Axios 收到)的数据连接到一个数组。我正在使用 React 钩子来更新状态。出于某种原因,即使 personObject 在控制台中显示正常(有 4 个元素,每个元素都有一个名称和一个 ID),当我 console.log 时会弹出一个空数组。这是 useEffect 代码。

const [ persons, setPersons] = useState([])

useEffect(() => {

console.log('effect')
  axios
    .get('http://localhost:3001/persons')
    .then(response => {
      console.log('promise fulfilled')
      const temp = response.data

      temp.forEach(function(e) {
        let personObject = {
          content: e.name,
          id: e.id,
        }
        console.log(personObject)
        setPersons(persons.concat(personObject))

      })

      console.log(persons)
    })
}, []) ```



【问题讨论】:

  • 技术细节,但非常重要的一点:那不是 JSON。根据定义,JSON 是字符串数据。一旦被解析,它就是普通的 JS 数据,就像您自己编写的任何其他代码一样。

标签: javascript reactjs


【解决方案1】:

更新状态是异步的。例如:

const state  = {
  myValue: 1
}

const someFunctionThatUpdatesState = () => {
    console.log("before", state.myValue)
    setState({ myValue: 2})
    console.log("after", state.myValue)
}

您认为控制台显示了什么?

您可能希望它记录before, 1after, 2,但实际记录的是before, 1after, 1

当您设置状态时,您实际上并没有设置状态,您是在安排状态更新。您基本上是在告诉 React 需要将状态更新为您传递给 setState 的值。

在组件重新渲染之前,React 实际上不会更新状态,因此在您的情况下,persons 只有在函数结束并且组件更新后才会具有新值。

说起来,你的函数有缺陷:

 setPersons(persons.concat(personObject))

由于persons 在重新渲染之前不会更改,因此您基本上取消了每次迭代的setState 调用。您实际上是在这样做:

setPersons([].concat(personObject1))
setPersons([].concat(personObject2))
setPersons([].concat(personObject3))
setPersons([].concat(personObject4))

您的结果最终将成为一个数组,其中只有最终值,其余的都将被覆盖。

你想要的是这样的:

 .then(response => {
  console.log('promise fulfilled')

  // make sure data actually exists
  if (response && response.data) {
      // use prev, which is the up-to-date state value
      setPersons(prev => [
          // spread the current value of `prev`
          ...prev,
          // map the data and spread the resulting array
          ...response.data.map(item => ({
              id: item.id,
              content: item.name,
          })),
      ])
  }
})

我所做的更改是:

  • 检查数据是否存在!如果 dataundefined 会发生什么?
  • 使用map 而不是forEach。您正在做的是将数据数组映射到状态数组,因此只需使用 map 函数而不是 forEach
  • concat 替换为扩展语法...。它更简洁,更易于阅读。
  • 只调用一次setPersons,然后将函数传递给setPersons

如果您的新状态值依赖于旧状态(如persons,您将新数据附加到旧数据数组中),您应该将一个函数传递给setStateprev 是最新的状态值,因此您可以依赖它。做类似setPersons(persons....) 的事情(在setPersons 中使用persons 很容易出错。

【讨论】:

  • 感谢有关将函数传递给 setPersons() 以获取 prev 值的提示。
【解决方案2】:

听起来您正在尝试获取数据,使用该数据创建一个 newPersons 数组,并将 newPersons 添加到现有 persons 数组的末尾。看看这是否对你有用:

const [persons, setPersons] = useState([])

useEffect(() => {
    console.log('effect');
    axios
        .get('http://localhost:3001/persons')
        .then(response => {
            console.log('promise fulfilled');
            const newPersons = response.data.map(person => ({
                content: person.name,
                id: person.id
            }));
            setPersons(prev => [...prev, ...newPersons]);
        });
}, []);

【讨论】:

    猜你喜欢
    • 2020-06-27
    • 2021-07-19
    • 2020-11-02
    • 2019-09-23
    • 1970-01-01
    • 2020-06-18
    • 1970-01-01
    • 2014-04-16
    相关资源
    最近更新 更多