我正在尝试从我的顶层组件实现动态主题化(根据React Router文档)(实际上,当选择菜单项但未发生重定向时主题发生了变化)-这意味着状态很少我的顶层组件需要管理。
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”,将单击一个菜单项,该菜单项将导致顶层状态更改,并使用正确的颜色重新渲染组件,但子级的其他方面则不会更改(因为没有变化影响他们)
这正是我想要的,但是在添加经过身份验证的路由时,使用了render
上的Route
道具。这里的问题是,当顶层组件中的状态发生变化时,子代将完全卸载并重新安装。这会导致状态丢失,并且用户当前看到的所有内容都会丢失。
更新的代码:
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;
Login.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;
我的问题是我该如何解决?我认为问题是由于从函数生成受保护的路由而引起的,导致父级中的状态更改/重新呈现会导致函数重新运行并返回新组件。那么,如何在功能之外/不进行重新渲染的情况下渲染这些受保护的路由,导致它们被卸载/重新装载?
编辑:现在可以了-谢谢。但是,现在登录成功后,就不会从“登录”组件将用户重定向到上一页(在添加此修复程序之前,此部分至少可以正常工作)。 props.history
存在并且没有错误,但是URL不会更改,除非我将forceRefresh
添加到我不能做的路由器中。
我已经编辑了更新的代码块,并从Login.js添加了Login功能
答案 0 :(得分:5)
那么我该如何在A之外渲染这些受保护的路由 功能/没有重新渲染导致它们被卸载/重新安装?
我不确定这是问题的根源,但是您是否尝试过在<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
:<Router history={history}
的支持。所有这些操作都已完成,因此您可以拥有一个独立的历史对象,现在需要将其导出。因此,有一些解决方法:
* export const history = createHistory();
现在您已经拥有并导出了独立的历史记录对象,可以在login.js
上导入和使用该对象:
import {history} from './app.jsx';
(输入正确的路线)就是这样。现在,您可以在login.js和该文件中的函数中使用它。