You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用React-Redux通过ID查找数据时返回undefined问题求助

React-Redux博客详情页获取数据问题

问题描述

我正在使用React-Redux开发基础博客CRUD应用,博客列表设有“阅读更多”按钮,点击后跳转对应博客详情页。尝试通过useParams Hook获取博客ID,调用findBlog action从Redux状态的博客列表中查找对应博客。目前能正确获取博客ID,但调用actions.findBlog(id)后,console.log(blog)显示undefined。

相关代码

动作创建器(Actions)

export const listBlog = (blogList) => {
    return (dispatch) => {
        dispatch({
            type: "list-blog",
            payload: blogList
        })
    }
}

export const addBlog = (blog) => {
    return (dispatch) => {
        dispatch({
            type: "add-blog",
            payload: blog
        })
    }
}

export const deleteBlog = (id) => {
    return (dispatch) => {
        dispatch({
            type: "delete-blog",
            payload: id
        })
    }
}

export const findBlog = (id) => {
    return (dispatch) => {
        dispatch({
            type: "find-blog",
            payload: id
        })
    }
}

reducer - blogReducer

import blogs from "../data"
const reducer = (state=blogs, action) => {
    if (action.type === "list-blog") {
        return state
    }
    else if (action.type === "add-blog"){
        state.push(action.payload)
        return state
    }
    else if (action.type === "delete-blog") {
        state.pop(action.payload)
        return state
    }
    else if (action.type === "find-blog") {
        for(let i=0; i<=state.length; i++){
            if(state[i].id === action.payload){
                console.log(state[i])
                return state[i]
            }
            else{
                return "Blog not Found"
            }
        }
    }
    else {
        return state
    }
}

export default reducer

博客详情页 - BlogPage.js

import React, { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { useParams } from "react-router-dom";
import { bindActionCreators } from "redux";
import { actionCreators } from "../State/index";

export const BlogPage = () => {

    const [blogId, setBlogId] = useState();

    const dispatch = useDispatch();
    const actions = bindActionCreators(actionCreators, dispatch)
    const params = useParams()
    
    useEffect(() => {
        setBlogId(params.blogId)
    },[])

    const handleEdit = (id) => {
        console.log(id)
        const blog = actions.findBlog(id)
        console.log(blog)
    }

    return (
        <div className="container">
            <h3></h3>
            <p>
                Body
            </p>
            <button className="btn btn-success my-3" onClick={() => handleEdit(blogId)}>Edit Blog</button>
            <button className="btn btn-danger mx-3">Delete Blog</button>
            <hr className="my-3"/>
            <button className="btn btn-primary">Like</button>
            <br /> <br />
            <h4 className="mx-3">Comments</h4>

        </div>
    )
}

问题原因

  1. Redux Action调用不返回数据:actions.findBlog(id)是触发状态更新的dispatch操作,本身不会返回目标博客数据——Redux的工作流是action触发reducer更新状态,组件需要通过useSelector从store中读取更新后的状态。
  2. Reducer逻辑错误:
    • find-blog分支的for循环条件i<=state.length会导致数组越界(数组索引最大为state.length-1);
    • 循环中第一次不匹配就直接返回"Blog not Found",不会继续遍历后续元素;
    • 直接返回单个博客或字符串会替换整个Redux状态(原本是数组),破坏状态结构。
  3. 状态修改不遵循不可变性:add-blog中用state.push、delete-blog中用state.pop都是直接修改原数组,不符合Redux状态不可变的要求,可能导致状态更新不触发组件重渲染。
  4. delete-blog逻辑错误:pop方法不接收参数,只会删除数组最后一个元素,无法根据id删除指定博客。

解决方案

1. 重构Reducer,修正状态结构与逻辑

将状态改为包含博客列表和选中博客的对象,确保状态不可变,修复查找、增删逻辑:

import blogs from "../data"

// 初始状态改为对象,包含博客列表和选中的博客
const initialState = {
    blogList: blogs,
    selectedBlog: null
}

const reducer = (state = initialState, action) => {
    switch(action.type) {
        case "list-blog":
            return state;
        case "add-blog":
            // 返回新数组,保持不可变性
            return {
                ...state,
                blogList: [...state.blogList, action.payload]
            };
        case "delete-blog":
            // 根据id过滤数组,删除指定博客
            return {
                ...state,
                blogList: state.blogList.filter(blog => blog.id !== action.payload)
            };
        case "find-blog":
            // 用find方法查找对应id的博客,找不到则返回null
            const foundBlog = state.blogList.find(blog => blog.id === action.payload) || null;
            return {
                ...state,
                selectedBlog: foundBlog
            };
        default:
            return state;
    }
}

export default reducer

2. 修改组件,正确获取Redux状态

使用useSelector读取选中的博客,移除不必要的state存储blogId:

import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useParams } from "react-router-dom";
import { bindActionCreators } from "redux";
import { actionCreators } from "../State/index";

export const BlogPage = () => {
    const dispatch = useDispatch();
    const actions = bindActionCreators(actionCreators, dispatch);
    const params = useParams();
    // 从Redux状态中获取选中的博客
    const selectedBlog = useSelector(state => state.blog.selectedBlog);

    // 组件挂载时自动查找对应博客
    useEffect(() => {
        if (params.blogId) {
            actions.findBlog(params.blogId);
        }
    }, [params.blogId, actions]);

    const handleEdit = () => {
        console.log("选中的博客:", selectedBlog);
        // 在这里处理编辑逻辑
    }

    return (
        <div className="container">
            <h3>{selectedBlog ? selectedBlog.title : "加载中..."}</h3>
            <p>
                {selectedBlog ? selectedBlog.body : ""}
            </p>
            <button className="btn btn-success my-3" onClick={handleEdit}>Edit Blog</button>
            <button className="btn btn-danger mx-3">Delete Blog</button>
            <hr className="my-3"/>
            <button className="btn btn-primary">Like</button>
            <br /> <br />
            <h4 className="mx-3">Comments</h4>
        </div>
    )
}

3. 额外优化点

  • 移除bindActionCreators,直接使用dispatch调用action:
    // 替换bindActionCreators的写法
    const findBlog = (id) => dispatch(actionCreators.findBlog(id));
    
  • 如果博客ID是字符串类型,确保与Redux中存储的id类型一致(比如都是字符串或数字),避免匹配失败。

内容的提问来源于stack exchange,提问作者Innomight

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 01:00:55