我正在尝试按每个博客的点赞数排列我的程序列表博客文章

I am trying to make my program list blog posts in order by the number of likes each blog has

我目前在一个博客网站上工作,我想根据每个博客的点赞数按升序列出博客。但是,每当我 运行 当前代码时,每当我单击一个 post 的“查看”时,所有 post 都会展开,而我只想展开其中一个 post ,此外,每当我点击“喜欢”时,所有 post 都会留下一个喜欢,而不仅仅是我喜欢的那个。

这是我在 App.js 中的代码:

import { useState, useEffect } from 'react'
import Blog from './components/Blog'
import blogService from './services/blogs'
import loginService from './services/login'
import BlogForm from './components/BlogForm'
import LoginForm from './components/LoginForm'
import Togglable from './components/Toggable'

const App = () => {
  const [blogs, setBlogs] = useState([])
  const [user, setUser] = useState('')
  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const [errorMessage, setErrorMessage] = useState('')

  useEffect(() => {
    blogService.getAll().then(blogs =>
      setBlogs( blogs )
    )  
  }, [errorMessage])

  useEffect(() => {
    const loggedUserJSON = window.localStorage.getItem('user')
    if (loggedUserJSON) {
      const user = JSON.parse(loggedUserJSON)
      setUser(user)
      blogService.setToken(user.token)
    }
  }, [])

  const handleLogin = async (e) => {
    e.preventDefault();
    try {
      const user = await loginService.login({
        username, password
      })

      window.localStorage.setItem('user', JSON.stringify(user))

      blogService.setToken(user.token)
      setUser(user)
      setUsername('')
      setPassword('')
    } catch (exception) {
      setErrorMessage('Wrong credentials')
      setTimeout(() => {
        setErrorMessage(null)
      }, 5000)
    }
  }

  const handleLogout = (e) => {
    e.preventDefault();
    window.localStorage.clear()
    setUser('')
  }

    if (user === '') { 
      return (
        <Togglable buttonLabel='login'>
              <LoginForm
              username={username}
              password={password}
              handleUsernameChange={({ target }) => setUsername(target.value)}
              handlePasswordChange={({ target }) => setPassword(target.value)}
              handleSubmit={handleLogin}
              errorMessage={errorMessage}
            />
        </Togglable>
      )} else {
          return (
            <div>
                <h2>blogs</h2>
                <h3>{errorMessage}</h3>
                <nobr>{user.name} logged in</nobr> <button onClick={handleLogout}>logout</button>
                <h2>create new</h2>
                <Togglable buttonLabel='create new'>
                  <BlogForm errorMessage={errorMessage} setErrorMessage={setErrorMessage} />
                </Togglable>
                  <Blog blogs={blogs} setErrorMessage={setErrorMessage}/>
                </div>
              )
        }
}

export default App

最后,这是我专门处理每个博客的代码:

import { useEffect, useState, useRef } from "react"
import blogService from '../services/blogs'
const baseUrl = '/api/blogs'

const Blog = ({blogs, setErrorMessage}) => {
  const [checker, setChecker] = useState(false)
  const [blogLikes, setBlogLikes] = useState(0)
  const buttonText = checker  ? 'hide' : 'view'

  const blogStyle = {
    paddingTop: 10,
    paddingLeft: 2,
    border: 'solid',
    borderWidth: 1,
    marginBottom: 5
  }

  return (
    <>
  {blogs.map(blog => {
    return (
      <>
  {buttonText === "view" ?   
  <div style={blogStyle}>
    {blog.title} {blog.author} <button onClick={() => setChecker(!checker)}>{buttonText}</button>
  </div>
  : <div style={blogStyle}>
      {blog?.title} {blog.author} <button onClick={() => setChecker(!checker)}>{buttonText}</button>
      <p>{blog.url}</p>
      likes {blogLikes} <button onClick={(e, blog) => {
        e.preventDefault()
        setBlogLikes(blogLikes + 1)
        blogService.update(blog?.id, {
          user: blog.user?.id,
          likes: blogLikes,
          author: blog.author,
          title: blog.title,
          url: blog.url})
        setErrorMessage(`You liked ${blog.author}s post`)
      }}>like</button>
      <p>{blog.user?.username}</p>
    </div>
    }</>)})}
    </>
  )
}
export default Blog

您在这里犯的错误是您创建了 Blog.js(或您为源文件指定的任何名称),您将其视为一个单独的博客,并期望“blogLikes”适用于一个单独的博客.

需要什么结构。


为列表和项目创建单独的组件,并将列表组件包含到应用程序中。 快速修复将是,

App.js

import { useState, useEffect } from 'react'
import BlogList from './components/Blog'
import blogService from './services/blogs'
import loginService from './services/login'
import BlogForm from './components/BlogForm'
import LoginForm from './components/LoginForm'
import Togglable from './components/Toggable'

const App = () => {
  const [blogs, setBlogs] = useState([])
  const [user, setUser] = useState('')
  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const [errorMessage, setErrorMessage] = useState('')

  useEffect(() => {
    blogService.getAll().then(blogs =>
      setBlogs( blogs )
    )  
  }, [errorMessage])

  useEffect(() => {
    const loggedUserJSON = window.localStorage.getItem('user')
    if (loggedUserJSON) {
      const user = JSON.parse(loggedUserJSON)
      setUser(user)
      blogService.setToken(user.token)
    }
  }, [])

  const handleLogin = async (e) => {
    e.preventDefault();
    try {
      const user = await loginService.login({
        username, password
      })

      window.localStorage.setItem('user', JSON.stringify(user))

      blogService.setToken(user.token)
      setUser(user)
      setUsername('')
      setPassword('')
    } catch (exception) {
      setErrorMessage('Wrong credentials')
      setTimeout(() => {
        setErrorMessage(null)
      }, 5000)
    }
  }

  const handleLogout = (e) => {
    e.preventDefault();
    window.localStorage.clear()
    setUser('')
  }

    if (user === '') { 
      return (
        <Togglable buttonLabel='login'>
              <LoginForm
              username={username}
              password={password}
              handleUsernameChange={({ target }) => setUsername(target.value)}
              handlePasswordChange={({ target }) => setPassword(target.value)}
              handleSubmit={handleLogin}
              errorMessage={errorMessage}
            />
        </Togglable>
      )} else {
          return (
            <div>
                <h2>blogs</h2>
                <h3>{errorMessage}</h3>
                <nobr>{user.name} logged in</nobr> <button onClick={handleLogout}>logout</button>
                <h2>create new</h2>
                <Togglable buttonLabel='create new'>
                  <BlogForm errorMessage={errorMessage} setErrorMessage={setErrorMessage} />
                </Togglable>
                  <BlogList blogs={blogs} setErrorMessage={setErrorMessage}/>
                </div>
              )
        }
}

export default App

BlogList.js

要按喜欢对博客进行排序,您在映射之前应用 Array.sort()

import Blog from './Blog';

const BlogList = ({blogs, setErrorMessage}) => {
    return (
        <>
        {
            blogs.sort((a, b)=> a.likes > b.likes ? 1 : -1 ).map((blog, i) => <Blog key={i} blog={blog} setErrorMessage={setErrorMessage} />
        }
        </>
    )
}
export default BlogList;

Blog.js

import blogService from '../services/blogs';
import { useEffect, useState, useRef } from 'react';

const Blog = ({blog, setErrorMessage}) => {
    const [checker, setChecker] = useState(false)
    const [blogLikes, setBlogLikes] = useState(blog.likes)
    const buttonText = checker  ? 'hide' : 'view'
    
    const blogStyle = {
        paddingTop: 10,
        paddingLeft: 2,
        border: 'solid',
        borderWidth: 1,
        marginBottom: 5
    }
    
    return (
        <>
        {buttonText === "view" ?
        <div style={blogStyle}>
        {blog.title} {blog.author} <button onClick={() => setChecker(!checker)}>{buttonText}</button>
        </div>
        : <div style={blogStyle}>
        {blog?.title} {blog.author} <button onClick={() => setChecker(!checker)}>{buttonText}</button>
        <p>{blog.url}</p>
        likes {blogLikes} <button onClick={(e, blog) => {
            e.preventDefault()
            setBlogLikes(blogLikes + 1)
            blogService.update(blog?.id, {
                user: blog.user?.id,
                likes: blogLikes,
                author: blog.author,
                title: blog.title,
                url: blog.url})
                setErrorMessage(`You liked ${blog.author}s post`)
            }}>like</button>
            <p>{blog.user?.username}</p>
            </div>
        }
        </>
    )
}
export default Blog

由于您在博客组件中有单独的点赞状态,因此其操作或数据不会影响其他博客组件。


[更新] 如果您熟悉 context 然后尝试一下,对于初学者,您可以像操作一样移动到 App.js 并将其作为道具传递,就像您对 setErrorMessage 所做的那样。此外,在 App.js 中添加具有 blogs 依赖项的空 useEffect,因此其更改将导致 re-render.