React Redux 私有路由最佳实践
React Redux Private Route Best Practise
专用路由的最佳做法是什么?也许我做错了什么,但是当用户登录时我已经重定向到 /login 页面
我的第二个问题:这些版本中哪个更好或者你有更好的主意?
代码:
授权
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
isUserLoggedIn: null,
isLoading: false,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(me.pending, (state) => {
state.isLoading = true
})
.addCase(me.fulfilled, (state, action) => {
state.user = action.payload.userData
state.isUserLoggedIn = true
state.isLoading = false
})
.addCase(me.rejected, (state) => {
state.isUserLoggedIn = false
})
},
})
export const me = createAsyncThunk('auth/me', async () => {
try {
const user = await userService.getUserData()
return { userData: user.data.data }
} catch (error) {
const message =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString()
return message
}
})
案例 1:
应用
function App() {
const dispatch = useDispatch()
useEffect(() => {
dispatch(me())
}, [])
const auth = useSelector((state) => state.auth)
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes isAuthenticated={auth.isUserLoggedIn} />
</BrowserRouter>
</div>
)
}
export default App
路线
export const AppRoutes = ({ isAuthenticated }) => (
<Routes>
<Route
path='/login'
element={<Login />}
/>
<Route
path='/dashboard'
element={
<PrivateRoute isAuthenticated={isAuthenticated}>
<Stats />
</PrivateRoute>
}
/>
...
私有路由
export const PrivateRoute = ({ children, isAuthenticated }) => {
return isAuthenticated ? children : <Navigate to='/login' />
}
案例 2:
应用
function App() {
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
</div>
)
}
export default App
路线
export const AppRoutes = () => (
<Routes>
<Route
path='/login'
element={<Login />}
/>
<Route
path='/dashboard'
element={
<PrivateRoute>
<Stats />
</PrivateRoute>
}
/>
...
私有路由
export const PrivateRoute = ({ children }) => {
const dispatch = useDispatch()
const { isUserLoggedIn } = useSelector((state) => state.auth)
useEffect(() => {
if (isUserLoggedIn === null) {
dispatch(me())
}
}, [])
return isUserLoggedIn ? children : <Navigate to='/login' />
}
案例 1 或案例 2 更适合处理,或者您有更好的主意?
对于这 2 个想法,当我转到 /dashboard 时,它会很快重定向到 /login
我想要实现的是良好的实践、快速的验证,并等待我们从后端收到用户已通过身份验证的肯定响应
团队,你有什么建议?
编辑
新版本:
Redux
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
isUserLoggedIn: null,
isLoading: true,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(me.pending, (state) => {
state.isLoading = true
})
.addCase(me.fulfilled, (state, action) => {
state.user = action.payload.userData
state.isUserLoggedIn = true
state.isLoading = false
})
.addCase(me.rejected, (state) => {
state.isLoading = false
state.isUserLoggedIn = false
})
},
})
export const me = createAsyncThunk('auth/me', async ({}, thunkAPI) => {
try {
const user = await userService.getUserData()
return { userData: user.data.data }
} catch (error) {
const message =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString()
thunkAPI.rejectWithValue(message)
return message
}
})
私有路由
export const PrivateRoute = ({ children }) => {
const dispatch = useDispatch()
const { isLoading, isUserLoggedIn } = useSelector((state) => state.auth)
if (isLoading) return null
return isUserLoggedIn ? (
children
) : (
<Navigate
to='/login'
replace
/>
)
}
应用程序
function App() {
const dispatch = useDispatch()
const { isUserLoggedIn } = useSelector((state) => state.auth)
useEffect(() => {
if (isUserLoggedIn === null) {
dispatch(me())
}
}, [])
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
</div>
)
}
export default App
商店
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
import authReducer from '../features/auth/authSlice'
export const store = configureStore({
reducer: {
auth: authReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({ serializableCheck: false }),
})
Index.js
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
但是当我重新加载页面时,我会转到 /login
不要像 /dashboard
那样停留在同一页面,我该如何应对?
这两种方法都可以。您看到的问题是基于用于身份验证检查和重定向 prior 的初始 redux 状态值 运行 set授权状态。在确定身份验证状态之前,您将希望推迟重定向。使用状态呈现 null 或一些加载指示器,直到用户通过身份验证。
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
isUserLoggedIn: null,
isLoading: true, // <-- assume initially loading state from mouting
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(me.pending, (state) => {
state.isLoading = true
})
.addCase(me.fulfilled, (state, action) => {
state.user = action.payload.userData
state.isUserLoggedIn = true
state.isLoading = false
})
.addCase(me.rejected, (state) => {
state.isUserLoggedIn = false
})
},
});
在这两种实现之间,IMO 首选第二种,因为它可以减少组件耦合。我建议将两者结合起来。在 App
中检查用户并发送操作以设置身份验证状态,并在私有路由中检查身份验证状态。
function App() {
const dispatch = useDispatch();
const { isUserLoggedIn } = useSelector((state) => state.auth);
useEffect(() => {
if (isUserLoggedIn === null) {
dispatch(me());
}
}, []);
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
</div>
);
}
...
export const PrivateRoute = ({ children }) => {
const dispatch = useDispatch()
const { isLoading, isUserLoggedIn } = useSelector((state) => state.auth)
if (isLoading) return null; // <-- or loading spinner, etc...
return isUserLoggedIn ? children : <Navigate to='/login' replace />
}
第一个版本也可以实现同样的效果,你只需要将 isLoading
状态连同 isUserLoggedIn
状态作为 props 传递给 PrivateRoute
。
我发现了一个问题!呵呵!
看似愚蠢 && 简单但它开始工作了 ->
Redux
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
isUserLoggedIn: null,
isLoading: true,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(me.pending, (state) => {
state.isLoading = true
})
.addCase(me.fulfilled, (state, action) => {
state.user = action.payload.userData
state.isUserLoggedIn = true
state.isLoading = false
})
.addCase(me.rejected, (state) => {
state.isLoading = false
state.isUserLoggedIn = false
})
},
})
export const me = createAsyncThunk('auth/me', async (_, thunkAPI) => { // <- {} replaced to _ and this is it!
try {
const user = await userService.getUserData()
return { userData: user.data.data }
} catch (error) {
const message =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString()
thunkAPI.rejectWithValue(message)
return message
}
})
专用路由的最佳做法是什么?也许我做错了什么,但是当用户登录时我已经重定向到 /login 页面
我的第二个问题:这些版本中哪个更好或者你有更好的主意?
代码:
授权
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
isUserLoggedIn: null,
isLoading: false,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(me.pending, (state) => {
state.isLoading = true
})
.addCase(me.fulfilled, (state, action) => {
state.user = action.payload.userData
state.isUserLoggedIn = true
state.isLoading = false
})
.addCase(me.rejected, (state) => {
state.isUserLoggedIn = false
})
},
})
export const me = createAsyncThunk('auth/me', async () => {
try {
const user = await userService.getUserData()
return { userData: user.data.data }
} catch (error) {
const message =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString()
return message
}
})
案例 1:
应用
function App() {
const dispatch = useDispatch()
useEffect(() => {
dispatch(me())
}, [])
const auth = useSelector((state) => state.auth)
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes isAuthenticated={auth.isUserLoggedIn} />
</BrowserRouter>
</div>
)
}
export default App
路线
export const AppRoutes = ({ isAuthenticated }) => (
<Routes>
<Route
path='/login'
element={<Login />}
/>
<Route
path='/dashboard'
element={
<PrivateRoute isAuthenticated={isAuthenticated}>
<Stats />
</PrivateRoute>
}
/>
...
私有路由
export const PrivateRoute = ({ children, isAuthenticated }) => {
return isAuthenticated ? children : <Navigate to='/login' />
}
案例 2:
应用
function App() {
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
</div>
)
}
export default App
路线
export const AppRoutes = () => (
<Routes>
<Route
path='/login'
element={<Login />}
/>
<Route
path='/dashboard'
element={
<PrivateRoute>
<Stats />
</PrivateRoute>
}
/>
...
私有路由
export const PrivateRoute = ({ children }) => {
const dispatch = useDispatch()
const { isUserLoggedIn } = useSelector((state) => state.auth)
useEffect(() => {
if (isUserLoggedIn === null) {
dispatch(me())
}
}, [])
return isUserLoggedIn ? children : <Navigate to='/login' />
}
案例 1 或案例 2 更适合处理,或者您有更好的主意?
对于这 2 个想法,当我转到 /dashboard 时,它会很快重定向到 /login
我想要实现的是良好的实践、快速的验证,并等待我们从后端收到用户已通过身份验证的肯定响应
团队,你有什么建议?
编辑
新版本:
Redux
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
isUserLoggedIn: null,
isLoading: true,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(me.pending, (state) => {
state.isLoading = true
})
.addCase(me.fulfilled, (state, action) => {
state.user = action.payload.userData
state.isUserLoggedIn = true
state.isLoading = false
})
.addCase(me.rejected, (state) => {
state.isLoading = false
state.isUserLoggedIn = false
})
},
})
export const me = createAsyncThunk('auth/me', async ({}, thunkAPI) => {
try {
const user = await userService.getUserData()
return { userData: user.data.data }
} catch (error) {
const message =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString()
thunkAPI.rejectWithValue(message)
return message
}
})
私有路由
export const PrivateRoute = ({ children }) => {
const dispatch = useDispatch()
const { isLoading, isUserLoggedIn } = useSelector((state) => state.auth)
if (isLoading) return null
return isUserLoggedIn ? (
children
) : (
<Navigate
to='/login'
replace
/>
)
}
应用程序
function App() {
const dispatch = useDispatch()
const { isUserLoggedIn } = useSelector((state) => state.auth)
useEffect(() => {
if (isUserLoggedIn === null) {
dispatch(me())
}
}, [])
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
</div>
)
}
export default App
商店
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
import authReducer from '../features/auth/authSlice'
export const store = configureStore({
reducer: {
auth: authReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({ serializableCheck: false }),
})
Index.js
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
但是当我重新加载页面时,我会转到 /login
不要像 /dashboard
那样停留在同一页面,我该如何应对?
这两种方法都可以。您看到的问题是基于用于身份验证检查和重定向 prior 的初始 redux 状态值 运行 set授权状态。在确定身份验证状态之前,您将希望推迟重定向。使用状态呈现 null 或一些加载指示器,直到用户通过身份验证。
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
isUserLoggedIn: null,
isLoading: true, // <-- assume initially loading state from mouting
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(me.pending, (state) => {
state.isLoading = true
})
.addCase(me.fulfilled, (state, action) => {
state.user = action.payload.userData
state.isUserLoggedIn = true
state.isLoading = false
})
.addCase(me.rejected, (state) => {
state.isUserLoggedIn = false
})
},
});
在这两种实现之间,IMO 首选第二种,因为它可以减少组件耦合。我建议将两者结合起来。在 App
中检查用户并发送操作以设置身份验证状态,并在私有路由中检查身份验证状态。
function App() {
const dispatch = useDispatch();
const { isUserLoggedIn } = useSelector((state) => state.auth);
useEffect(() => {
if (isUserLoggedIn === null) {
dispatch(me());
}
}, []);
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
</div>
);
}
...
export const PrivateRoute = ({ children }) => {
const dispatch = useDispatch()
const { isLoading, isUserLoggedIn } = useSelector((state) => state.auth)
if (isLoading) return null; // <-- or loading spinner, etc...
return isUserLoggedIn ? children : <Navigate to='/login' replace />
}
第一个版本也可以实现同样的效果,你只需要将 isLoading
状态连同 isUserLoggedIn
状态作为 props 传递给 PrivateRoute
。
我发现了一个问题!呵呵!
看似愚蠢 && 简单但它开始工作了 ->
Redux
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
isUserLoggedIn: null,
isLoading: true,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(me.pending, (state) => {
state.isLoading = true
})
.addCase(me.fulfilled, (state, action) => {
state.user = action.payload.userData
state.isUserLoggedIn = true
state.isLoading = false
})
.addCase(me.rejected, (state) => {
state.isLoading = false
state.isUserLoggedIn = false
})
},
})
export const me = createAsyncThunk('auth/me', async (_, thunkAPI) => { // <- {} replaced to _ and this is it!
try {
const user = await userService.getUserData()
return { userData: user.data.data }
} catch (error) {
const message =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString()
thunkAPI.rejectWithValue(message)
return message
}
})