我遇到一个无限循环,我知道问题是因为我在useEffect函数中将“ posts”和“ setPost”作为第二个参数放在方括号中,但是每当我添加一个新帖子,因此帖子必须放在方括号中。
function Home() {
const {userData, setUserData} = useContext(userContext)
const [posts, setPost] = useState([])
const [createPost, setCreatePost] = useState('')
const handleToken = () => {
localStorage.removeItem('auth-token')
}
const token = localStorage.getItem("auth-token");
const handleOnSubmit = (e) => {
e.preventDefault()
axios.post('http://localhost:5000/posts', {textOfThePost: createPost}, {
headers: { 'auth-token': token },
})
.then((res) => {setCreatePost("")})
}
useEffect(() => {
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data)
})
}, [posts])
return (
<div className="home">
<div style={{display: 'flex', alignItems: 'center'}}>
<h1>this is the home: Welcome, {userData.username}</h1>
<Link style={{margin: 10}} to="/home">home</Link>
<Link style={{margin: 10}} to="/profile">profile</Link>
<Link style={{margin: 10}} onClick={handleToken} to="/">log out</Link>
</div>
<form onSubmit={handleOnSubmit}>
<input type="text" placeholder="What's happening?" value={createPost} onChange={e => setCreatePost(e.target.value)}/>
<button type="submit">tweet</button>
</form>
<div style={{display: 'flex', flexDirection: 'column'}}>
{posts.map(post => (
<div style={{border: '2px solid black', marginBottom: 10, marginRight: 'auto', marginLeft: 'auto', width: 300}} key={post._id}>
<div style={{display: 'flex', alignItems: 'center'}}>
<Avatar src={post.avatar}/>
<span style={{color: 'blue', marginLeft: 10}}>{post.name} <span style={{color: 'grey', fontSize: 11}}>@{post?.username}</span></span><br/>
</div>
<span>{post.textOfThePost}</span><br/>
<span>{moment(post.date).format('lll')}</span>
</div>
)).reverse()}
</div>
</div>
)
}
答案 0 :(得分:1)
这里的问题是useEffect
(和类似的钩子)的依赖项数组未使用深度比较(出于性能原因)。
也就是说,每当通过Axios获取新数据时,res.data
就是一个新的JavaScript对象,当您将其分配给状态时,效果依赖项将其视为完全更改的对象并再次运行效果,依此类推。 / p>
最简单的解决方法是使用比较深的useEffect
,例如https://github.com/kentcdodds/use-deep-compare-effect。
答案 1 :(得分:0)
您实际上没有使用posts
,因此它根本不应该在依赖数组中。解决您问题的一种方法可能是在第一个渲染中获取帖子,一次,然后在您创建帖子时,使用响应更新posts
状态。
const posts = [
{ id: "1", text: "foo" },
{ id: "2", text: "bar" },
{ id: "3", text: "baz" }
];
const API = {
getPosts: () =>
new Promise((resolve) => setTimeout(() => resolve(posts), 2000)),
createPost: () =>
new Promise((resolve) =>
setTimeout(() => resolve({ id: "3", text: "fizz" }), 1000)
)
};
function Posts() {
const [posts, setPosts] = React.useState([]);
React.useEffect(() => {
API.getPosts().then(setPosts);
}, []);
function handleOnSubmit() {
API.createPost().then((res) => setPosts((prev) => [...prev, res]));
}
return (
<div>
<div>
<button onClick={handleOnSubmit}>Create Post</button>
</div>
{!Boolean(posts.length) ? (
<span>Loading posts...</span>
) : (
posts.map((post) => <div>{post.text}</div>)
)}
</div>
);
}
ReactDOM.render(
<Posts />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root" />
请勿打扰API
部分,我只是在模仿您的要求。重要的部分是在创建帖子,使用响应并更新状态之后。