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

为什么React的state返回空值但页面却能正常渲染对应数据?

核心问题&修复方案

问题1:状态打印异常

React的setState是异步批量执行的,调用setPages后,当前作用域下的pages变量还是更新前的旧值,所以你在setPages之后立刻打印pages会拿到旧值(初始是空数组),但组件下一次渲染会拿到新的状态,所以map渲染是正常的。如果要打印更新后的状态,可新增useEffect监听pages变化触发打印。

问题2:新增页面只显示单条的根因

你给AddNewPageFunc.current赋值的useEffect依赖是空数组,只会在组件第一次渲染时执行一次,所以存到ref里的AddNewPage永远是初始版本,闭包捕获的pages永远是空数组。点击新增页面时,执行的是这个旧版本的AddNewPage,pages.concat(response.data)相当于空数组拼接新页面,所以状态里就只剩新增的页面,刷新后重新拉取全量数据才会正常显示。

问题3:删除操作逻辑错误

你在handleDelete里用了splice方法,这个方法是原地修改原数组,不符合React状态不可变的要求,而且splice返回的是被删除的元素数组,不是删除后的剩余数组,调用setPages(PageRemoved)会把状态改成只有被删的元素,逻辑完全错误。

优化点:闭包风险消除

movePage、AddNewPage里的setPages都可以改成函数式更新,传入的参数是最新的状态,不需要再依赖pages,减少不必要的函数重建,也能避免闭包问题。


修复后代码

function PageTree({AddNewPageFunc}) {
  const [pages, setPages] = useState([]);
  // 函数式更新消除pages依赖
  const movePage = useCallback((droppedPage) => {
    setPages(prevPages => prevPages.map(page => droppedPage._id == page._id ? droppedPage : page));
  }, []);

  const [{isOver}, drop] = useDrop(() => ({
        accept: ItemTypes.PAGECARD,
        drop(page, monitor) {
            const delta = monitor.getDifferenceFromInitialOffset();
            let x = Math.round(page.x + delta.x);
            let y = Math.round(page.y + delta.y);
            page.x = x;
            page.y = y;
            movePage(page);
            setNewPagePosition(page);
            return undefined;
        },
    }), [movePage]);

    const setNewPagePosition = async (pageDetails) => {
        console.log("function called to update page position");
        Api.withToken().post('/pageupdate/'+pageDetails._id,
            pageDetails
        ).then(function (response) {
            console.log("moved page: ",response.data)
        }).catch(function (error) {
          // 异常处理逻辑
        });
    }

    // 新增AddNewPage到依赖,每次函数更新就刷新ref
    React.useEffect(() => {
      AddNewPageFunc.current = AddNewPage
    }, [AddNewPage])

    const AddNewPage = useCallback(() => {
      console.log("calling add new page function")
      Api.withToken().post('/addblankpage/'
        ).then(function (response) {
          console.log("produced: ",response.data);
          // 函数式更新拿最新的状态拼接
          setPages(prevPages => prevPages.concat(response.data))
      }).catch(function (error) {
        // 异常处理逻辑
      });
    }, []); // 无外部依赖

    const handleDelete = async (id) => {
        Api.withToken().post('/deletepages/'+id
        ).then(function (response) {
            // filter返回新数组,不修改原状态
            setPages(prevPages => prevPages.filter(item => item._id !== response.data._id));
        }).catch(function (error) {
          // 异常处理逻辑
        });
    }

    useEffect(() => {
      Api.withToken().get('/pages/')
      .then(res => {
        setPages(res.data);
        console.log('res data ',res.data);
      })
    }, []);
  
  // 缺失的辅助逻辑可自行补充
  const handleMaximise = (page) => { /* 自定义逻辑 */ }
  const handleCopy = (page) => { /* 自定义逻辑 */ }
  const styles = { /* 自定义样式 */ }

  return (
      <div ref={drop} style={styles}>
          {pages.map((page) => (
            <PageCard 
              page={page} 
              id={page._id} 
              key={page._id} 
              handleDelete={() => handleDelete(page._id)} 
              handleMaximise={() => handleMaximise(page)} 
              handleCopy={() => handleCopy(page)}
            />
          ))}
      </div>
      )
}

export default PageTree;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 04:45:03