gpt4 book ai didi

javascript - react 路由器渲染导致卸载

转载 作者:塔克拉玛干 更新时间:2023-11-02 20:39:44 24 4
gpt4 key购买 nike

I'm trying to implement authenticated routes (as per React router docs) alongisde dynamic theming from my top level component (essentially the theme changes when a menu item is selected but no redirection occurs) - this means a small amount of state我的顶级组件需要管理。

function App() {
const [ state, setState ] = React.useState({
current_theme: themes['blue']
});

const [ logged_in, setLoggedIn ] = React.useState( !!Cookie.get('JWT') );

function selectTheme( theme ) {
setState({
current_theme: themes[theme]
});
};

return (
<MuiThemeProvider theme={state.current_theme}>
<>
{
logged_in && <Header selectTheme={ selectTheme }/>
}
<AppContainer logged_in={ logged_in }>
<Switch>
<Route exact path='/' component={ Home }/>
<Route exact path='/service_users' component={ ServiceUsers } />
</Switch>
</AppContainer>
</>
</MuiThemeProvider>
);
}

export default App;

在添加经过身份验证的路由之前,一切正常。例如,用户会在“/service_users”,会单击一个菜单项,这会导致顶级状态更改,并会使用正确的颜色重新呈现组件,但子项的其他方面不会更改(因为有没有影响他们的变化)

这正是我想要的,但是在添加经过身份验证的路由时,使用了 Route 上的 render 属性。这里的问题是,当顶级组件内的状态发生变化时,子组件将完全卸载并重新安装。这会导致状态丢失,用户当前看到的所有内容都会丢失。

更新代码:

function PrivateRoute({ component: Component, ...rest }) {
return (
<Route
{ ...rest }
render={ props => {
return logged_in ? (
<Component { ...props } />
) : (
<Redirect
to={{
pathname: '/login',
state: { from: props.location }
}}
/>
)
}}
/>
);
}

function App() {
const [ state, setState ] = React.useState({
current_theme: themes['blue']
});

const [ logged_in, setLoggedIn ] = React.useState( !!Cookie.get('JWT') );

function selectTheme( theme ) {
setState({
current_theme: themes[theme]
});
};

function login() {
setLoggedIn( true );
}

return (
<MuiThemeProvider theme={state.current_theme}>
<Router>
{
logged_in && <Header drawer_open={ drawer_open } selectTheme={ selectTheme }/>
}
<AppContainer logged_in={ logged_in }>
<Switch>
<PrivateRoute exact path='/' component={ ServiceUsers }/>
<PrivateRoute exact path='/service_users' component={ ServiceUsers } />
<Route exact path='/login' render={ ( props ) => <Login { ...props } setLogin={ login.bind( this ) }/> } />
</Switch>
</AppContainer>
</Router>
</MuiThemeProvider>
);
}

export default App;

登录.js:

function Login( props ) {
const classes = useStyles();
const [ state, setState ] = useState({
username: '',
password: ''
});
const [ loading, setLoading ] = useState( false );
const [ success, setSuccess ] = useState( !!Cookie.get('JWT') );

console.log( 'success: ', success );

function onInputChange( e ) {
setState({ ...state, [e.target.id]: e.target.value });
}

async function loginRequest( e ) {
e.preventDefault();

const { username, password } = state;

//TODO: validation of email/password
if( username.length < 5 || password.length < 5 )
return;

setLoading( true );
const res = await asyncAjax( 'POST', '/login', { username, password } );

setLoading( false );

if( res.status !== 200 )
console.log( 'ERROR' ); //TODO: add error handling

//Store JWT and systems
Cookie.set( 'JWT', `Bearer ${ res.token }`, { path: '/', days: 30 } );

//Use local storage for systems as likely to be much more data
localStorage.setItem( 'SYSTEMS', JSON.stringify( res.login.systems ) );

//Set login status and push user to referrer
props.setLogin();

props.history.push( from );
}

return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<HeaderLogo/>
<form className={classes.form} noValidate>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="username"
label="Email Address"
name="username"
autoComplete="username"
autoFocus
onChange={ onInputChange }
/>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
onChange={ onInputChange }
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick={ loginRequest }
>
{
loading ? (
<CircularProgress
color='inherit'
size={ 25 }
style={{ color: '#FFF' }}
/>
) : (
'Login'
)
}
</Button>
<Grid container>
<Grid item xs>
<Link href="#" variant="body2">
Forgot password?
</Link>
</Grid>
</Grid>
</form>
</div>
</Container>
);
}

export default Login;

我的问题是如何解决这个问题?我认为问题的发生是因为 protected 路由是从函数生成的,导致父级中的状态更改/重新渲染导致函数重新运行并返回新组件。那么我如何在函数之外渲染这些 protected 路由/不重新渲染导致它们被卸载/重新安装?

编辑:现在可以了 - 谢谢。但是,现在当登录成功时,用户不会从登录组件重定向到上一个页面(这部分至少在添加修复之前工作正常)。 props.history 存在并且没有错误,但 URL 不会更改,除非我将 forceRefresh 添加到路由器,但我无法这样做。

我编辑了更新后的代码块并从 Login.js 添加了登录功能

最佳答案

So how do i render these protected routes outside of a function/without a re-render causing them to be unmounted/re-mounted?

我不确定这是你问题的根源,但你是否尝试过只构建一个 <PrivateRoute /> <App /> 之外的组件组件?

像这样:

function App() {
const [ state, setState ] = React.useState({
current_theme: themes['blue']
});

const [ logged_in, setLoggedIn ] = React.useState( !!Cookie.get('JWT') );

function selectTheme( theme ) {
setState({
current_theme: themes[theme]
});
};

function login() {
setLoggedIn( true );
}

return (
<MuiThemeProvider theme={state.current_theme}>
<>
{
logged_in && <Header selectTheme={ selectTheme }/>
}
<AppContainer logged_in={ logged_in }>
<Switch>
<PrivateRoute exact path='/' component={ ServiceUsers } logged_in={logged_in}, log_in={login}/>
<PrivateRoute exact path='/service_users' component={ ServiceUsers } logged_in={logged_in}, log_in={login} />
<Route exact path='/login' render={ ( props ) => <Login { ...props } setLogin={ login.bind( this ) }/> } />
</Switch>
</AppContainer>
</>
</MuiThemeProvider>
);
}

function PrivateRoute({ component: Component, logged_in, log_in, ...rest }) {
return (
<Route
{ ...rest }
render={ props => {
return props.logged_in ? (
<Component { ...props } />
) : (
<Redirect
to={{
pathname: '/login',
setLogin: log_in.bind( this ),
state: { from: props.location, test: 'test' }
}}
/>
)
}

}
/>
);
}

export default App;

编辑:

既然您已经提到了,我想解决您的路由重定向问题。我认为问题在于 history函数不能独立访问对象。我不确定为什么以前可以使用它,因为您没有共享更多代码和分解结构。

无论如何,以下步骤应该允许您使用一个独立的和导出的历史对象:

  • 安装历史包:npm i --save history .

<App /> 所在的文件中组件是做:

  • import createHistory from 'history/createBrowserHistory' .
  • const history = createHistory();
  • 使用Router而不是 BrowserRouter : import {Router} from 'react-router';
  • 传递history对象作为 Router 的 Prop : <Router history={history} .

所以所有这些都完成了,这样你就可以拥有一个独立的历史对象,现在你需要导出它。所以一点点修复: * export const history = createHistory();

现在您已经导出了独立的历史对象,您可以在 login.js 导入和使用它:

  • import {history} from './app.jsx'; (当然要放上自己的正确路径)

就是这样。现在您可以在 login.js 和该文件中的函数中使用它。

关于javascript - react 路由器渲染导致卸载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57480420/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com