我正在重构一些代码,现在我被卡住了。 原始代码来自tuto: https://github.com/EricLondon/rails5-react-reactstrap-crud-example 那里一切都很好。 我尝试使用钩子的新功能。 我写的新代码失败了。
“原始代码”已通过测试 “新代码”失败
//"New code"
import React, { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { Container, Row, Col, Alert } from 'reactstrap'
import PostsTable from '../PostsTable'
const Api = require('../../api/Api.js')
export default function Posts() {
const [posts, setPosts] = useState([])
const [isLoaded, setIsLoaded] = useState(false)
const [error, setError] = useState(null)
useEffect( () => {
Api.getPosts()
.then( response => {
const [errata, data] = response
if (errata) {
setIsLoaded(true)
setPosts([])
setError(data)
} else {
setIsLoaded(true)
setPosts(data)
}
})
})
if (error) {
return (
<Alert color="danger">
Error: {error}
</Alert>
)
} else if (!isLoaded) {
return (
<Alert color="primary">
Loading...
</Alert>
)
} else {
return (
<Container>
<Row>
<Col>
<PostsTable posts={posts}></PostsTable>
<Link className="btn btn-primary" to="/posts/new">Add Post</Link>
</Col>
</Row>
</Container>
)
}
}
//"Original code"
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import { Container, Row, Col, Alert } from 'reactstrap'
import PostsTable from '../PostsTable'
const Api = require('../../api/Api.js')
class Posts extends Component {
constructor(props) {
super(props)
this.state = {
posts: [],
isLoaded: false,
error: null
}
}
componentDidMount() {
Api.getPosts()
.then(response => {
const [error, data] = response
if (error) {
this.setState({
isLoaded: true,
posts: [],
error: data
})
} else {
this.setState({
isLoaded: true,
posts: data
})
}
})
}
render() {
const { error, isLoaded, posts } = this.state
if (error) {
return (
<Alert color="danger">
Error: {error}
</Alert>
)
} else if (!isLoaded) {
return (
<Alert color="primary">
Loading...
</Alert>
)
} else {
return (
<Container>
<Row>
<Col>
<PostsTable posts={posts}></PostsTable>
<Link className="btn btn-primary" to="/posts/new">Add Post</Link>
</Col>
</Row>
</Container>
)
}
}
}
export default Posts
使用“新代码”,我的后端服务器发疯了: .... 在2019-05-26 20:45:39 +0200开始为127.0.0.1获取GET / api / posts 由Api :: PostsController#index处理为HTML 发布后加载(1.3ms)选择“帖子”。*从“帖子” ↳app / controllers / api / posts_controller.rb:5 6ms内完成200 OK(查看:4.4ms | ActiveRecord:1.3ms) .... 等
答案 0 :(得分:1)
我认为问题已经解决。谢谢。 实际上,我们同时面临两个问题。 1 /第二个参数中的[]是必要的, 2 /然后,正如@olivier所说,子组件中有一些错误代码:PostsTable。 “原始代码”:
class PostsTable extends Component {
constructor(props) {
super(props)
this.state = {
posts: props.posts
}
}
render() {
const posts = this.state.posts
if (posts.length === 0) {
return <div></div> ......
我这样做了: “新代码”:
class PostsTable extends Component {
render() {
const posts = this.props.posts
if (posts.length === 0) {
return <div></div> .....
我只用道具渲染PostsTable。 PostsTable现在是无状态的。 注意:在埃里克·伦敦(Eric London)的原始代码中,我认为PostsTable对象是在全局设置状态内创建的。 使用useEffect和setIsLoaded(true); setPosts(data)更加棘手,因为一旦启动setIsLoaded(true),就会创建PostsTable对象并进行渲染,而无需再次调用构造函数。
我结束了这样的重构:
export default function PostsTable(props) {
const posts = props.posts
if (posts.length === 0) {
return <div></div> ....
thx @olivier
答案 1 :(得分:0)
现在,您的`useEffect在每个渲染器上运行。您应该添加一个空数组作为第二个参数,以便仅在mount上运行:
useEffect(() => {
...
}, []);
通过提供一个数组作为第二个参数,您告诉useEffect
函数“只要这些值之一发生更改,就再次运行”。如果根本不提供数组,它将在每次组件渲染时运行(在这种情况下,将调用setIsLoading
,setError
或setPosts
)。>
为它提供一个空的依赖项数组仍会告诉该函数“这些值之一随时随地运行”,但是由于没有值会改变,因此只会运行一次。
答案 2 :(得分:0)
默认情况下,传递给使用Effect的函数将在每次渲染组件后执行。
当您在效果内部更新状态变量时,它将触发组件的重新渲染,然后效果将再次执行,基本上是无限循环。
如果将空数组传递给useEffect
,则将只执行一次效果,因为只有在数组中的一个值发生更改时,效果才会重新执行。
以下代码可以解决问题
useEffect( () => {
Api.getPosts()
.then( response => {
const [errata, data] = response
if (errata) {
setIsLoaded(true)
setPosts([])
setError(data)
} else {
setIsLoaded(true)
setPosts(data)
}
})
}, [])
答案 3 :(得分:0)
useEffect( () => {
Api.getPosts()
.then( response => {
const [errata, data] = response
if (errata) {
setIsLoaded(true)
setPosts([])
setError(data)
console.log('-+- showing data1 -+-')
console.log(data)
} else {
setIsLoaded(true)
setPosts(data)
console.log('-+- showing data2 -+-')
console.log(data)
}
})
},[])
显示'-+-显示数据2-+- (5)[{…},{…},{…},{…},{…}] 0:{id:14,标题:“ qsdf”,正文:“ qsdf”,created_at:“ 2019-05- 26T13:07:58.006Z“,更新日期:” 2019-05-26T13:07:58.006Z“} 1:{id:15,标题:” qsdf“,正文:”鸡蛋“,created_at:” 2019-05-26T17 :47:48.323Z“,updated_at:” 2019-05-26T17:47:48.323Z“} 2:{id:16,标题:” qdf“,正文:” qdf“,created_at:” 2019-05-26T17: 48:52.257Z“,更新日期:” 2019-05-26T17:48:52.257Z“} 3:{id:17,标题:” qsdf“,正文:” h“,created_at:” 2019-05-26T19:49 :41.108Z“,更新日期:” 2019-05-26T19:49:41.108Z“} 4:{id:18,标题:” qsdf“,正文:” 123“,created_at:” 2019-05-26T19:59: 38.002Z“,更新日期:” 2019-05-26T19:59:38.002Z“}长度:5__proto __:Array(0)
答案 4 :(得分:-1)
很抱歉其他答案,但空数组不正确。您可以阅读Dan Abramov(Redux和React Hooks的创建者)撰写的长达49分钟的文章,这是为什么。
https://overreacted.io/a-complete-guide-to-useeffect/
我相信您可以像这样组合所有useState挂钩来解决问题:
export default function Posts() {
const [state, setState] = useState({posts: [],
isLoaded: false,
error: null })
useEffect( () => {
Api.getPosts()
.then( response => {
const [errata, data] = response
if (errata) {
setState({ ...state,
isLoaded: true,
posts: [],
error: data })
} else {
setState({ ...state,
isLoaded: true,
posts: [data],
error: null })
}
})
}, [state.isLoaded] )
请确保您使用扩展语法传递了先前的状态,否则您的先前状态将不会保存。
替代方法在useEffect中使用条件,并将整个状态作为对useEffect的依赖项传递
export default function Posts() {
const [state, setState] = useState({posts: [],
isLoaded: false,
error: null })
useEffect( () => {
if(!state.isLoaded) {
Api.getPosts()
.then( response => {
const [errata, data] = response
if (errata) {
setState({ ...state,
isLoaded: true,
posts: [],
error: data })
} else {
setState({ ...state,
isLoaded: true,
posts: [data],
error: null })
}
})
}
}, [state] )