为什么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
相关产品推荐
相关产品推荐

