React中useContext+useEffect持久化localStorage数据失败问题
React中useContext结合useEffect持久化数据遇条件渲染失效问题
我在React中用useContext配合useEffect,想把useContext里的数据持久化到localStorage,但因为条件渲染导致功能失效。需求很明确:用户未登录时显示登录按钮,登录后切换成登出按钮。
目前已经实现了点击弹窗内的登录按钮,把user的初始null状态改成“User is logged in”。但遇到了问题:
在SignInDialog.js里我写了一个依赖弹窗open属性的useEffect,原本预期弹窗打开或关闭时都会打印“useEffect setItem 2”,但实际只有打开弹窗后点击侧边关闭时这个useEffect能正常触发;点击登录按钮关闭弹窗后,这个useEffect完全没触发。
排查后发现,user的值被用于父组件Main.js的条件渲染,只要把这个条件渲染去掉,所有功能就恢复正常了。我怀疑是父子组件的渲染顺序导致的问题,但暂时找不到不改动现有实现方案的修复方法。
相关代码
UserContext.js
import { createContext } from "react"; export const UserContext = createContext(null);
App.js
export default function App() { const [user, setUser] = useState(); return ( <div className="App"> <UserContext.Provider value={{ user, setUser }}> <Main /> </UserContext.Provider> </div> ); }
Main.js
export default function Main() { const { user, setUser } = useContext(UserContext); const logOut = () => { setUser(null); }; return ( <div className="App"> {/* 只保留SignInButton不加条件渲染的话功能正常 */} {/* <div> <SignInButton /> </div> */} {user ? ( <div> <Typography>{user}</Typography> <Button onClick={logOut}>Log out</Button> </div> ) : ( <div> <SignInButton /> </div> )} </div> ); }
SignInButton.js
const SignInButton = () => { const [openSignInDialog, setOpenSignInDialog] = useState(false); const handleSignInClickOpen = () => { setOpenSignInDialog(true); }; return ( <StyledEngineProvider injectFirst={true}> <div> <Button onClick={handleSignInClickOpen}> Sign in </Button> <SignInDialog open={openSignInDialog} setOpen={setOpenSignInDialog} /> </div> </StyledEngineProvider> ); }; export default SignInButton;
SignInDialog.js
export default function SignInDialog(props) { const { user, setUser } = useContext(UserContext); useEffect(() => { console.log(">>>>>>>>useEffect getItem 1"); /* const data = window.localStorage.getItem('uli') console.log(`data = ${data}`) */ }, []); useEffect(() => { console.log(">>>>>>>>useEffect setItem 2"); /* window.localStorage.setItem('uli', user) */ }, [props.open]); { /* 最终目标是把user作为依赖,现在先这样留着测试 */ } const test = () => { setUser("User logged in"); props.setOpen(false); { /* 注释掉props.setOpen(false)也没区别 */ } }; const handleClose = () => { props.setOpen(false); }; return ( <div className="App"> <Dialog open={props.open} onClose={handleClose}> <Box component="form"> <DialogTitle>Sign in data</DialogTitle> <DialogContent> <DialogContentText>username, password..</DialogContentText> </DialogContent> <DialogActions> <Button type="button" onClick={test}> Sign in </Button> </DialogActions> </Box> </Dialog> </div> ); }
内容的提问来源于stack exchange,提问作者hanku8
相关产品推荐
相关产品推荐

