使用受保护的路由在React中在按钮点击时导航。

huangapple go评论78阅读模式
英文:

Navigate on button click using protected routes in react

问题

以下是您提供的代码的中文翻译:

App.js

<Router>
     <Routes>
         <Route element={<AnonymousUserRoute />}>
              <Route path='/user/login' element={<UserLoginScreen />}/>
         </Route>
         <Route element={<ProtectedUserRoute />} >
              <Route path='/' element={<HomeScreen />} exact />
         </Route>
     </Routes>
</Router>

ProtectedUserRoute.js

function ProtectedUserRoute() {
    const loginPageLink = "/user/login";
    const token = //从本地存储获取

    return (
        token ? <Outlet /> : <Navigate to={loginPageLink} replace />
    );
};

AnonymousUserRoute.js

function AnonymousUserRoute() {
    const homePageLink = "/";
    const token = //从本地存储获取

    return (
        token ? <Navigate to={homePageLink} replace /> : <Outlet />
    );
};

Login.js

function UserLoginScreen() {
    ...
    const handleLoginClick = (event) => {
        event.preventDefault();
        dispatch(loginUser(email, password));
        navigate('/');
    }
    ...
}

reducers.js

export const userLoginReducer = (state = { userDetails: { isLoggedIn: false } }, action) => {
    switch(action.type) {
        case USER_LOGIN_REQUEST:
            return { loading: true }
        case USER_LOGIN_SUCCESS:
            return { loading: false, isLoggedIn: action.payload }
        case USER_LOGIN_FAIL:
            return { loading: false, error: action.payload }
        default:
            return state
    }
}

actions.js

export const loginUser = (email, password) => async (dispatch) => {
    const loginUrl = //登录URL
    try {
        dispatch({ type: USER_LOGIN_REQUEST })
        
        const loginData = { email: email, password: password }
        const config = { headers: { 'Content-type': 'application/json'} }
        const { data } = await axios.post(loginUrl, loginData, config)
        
        dispatch({ type: USER_LOGIN_SUCCESS, payload: true })
        
        storeUserLoggedInDetailsInLocalStorage(data.tokens); //调用一个将数据存储在本地存储中的函数。这里没有其他操作。
    } catch(error) {
        dispatch({
            type: USER_LOGIN_FAIL,
            payload: error.response && error.response.data.detail
                    ? error.response.data.detail
                    : error.message
        })
        console.log(error)
    }
}

store.js

const userDetailsFromStorage = localStorage.getItem('userDetails') ? 
                         JSON.parse(localStorage.getItem('userDetails')) : {}

const reducer = combineReducers({
    userDetails: userLoginReducer,
})

const persistedState = {
    userDetails: {
        isLoggedIn: userDetailsFromStorage.isLoggedIn ?? false
    }
}

const middleWare = [thunk]

const store = createStore(reducer, persistedState, composeWithDevTools(applyMiddleware(...middleWare)));

export default store

希望这些翻译有帮助。如果您有任何其他问题,请随时提问。

英文:

I have App.js which looks like this

<Router>
     <Routes>
         <Route element={<AnonymousUserRoute />}>
              <Route path='/user/login' element={<UserLoginScreen />}/>
         </Route>
         <Route element={<ProtectedUserRoute />} >
              <Route path='/' element={<HomeScreen />} exact />
         </Route>
     </Routes>
</Router>

ProtectedUserRoute.js

function ProtectedUserRoute() {
    const loginPageLink = "/user/login";
    const token = //getting from local storage

    return (
        token ? <Outlet /> : <Navigate to={loginPageLink} replace />
    );
};

AnonymousUserRoute.js

function AnonymousUserRoute() {
    const homePageLink = "/";
    const token = //getting from local storage

    return (
        token ? <Navigate to={homePageLink} replace /> : <Outlet />
    );
};

Login.js

function UserLoginScreen() {
    ...
    const handleLoginClick = (event) => {
        event.preventDefault();
        dispatch(loginUser(email, password));
        navigate('/');
    }
    ...
}

The problem I am facing is that, once handleLoginClick is clicked, the navigate function is not redirecting me to home screen immediately because ProtectedUserRoute doesn't allow i think. But upon refreshing the page, my route identifies that there is a token in local storage and redirects to home screen. How can i ensure, navigation happens without refresh here ?

EDIT

reducers.js

export const userLoginReducer = (state = { userDetails: { isLoggedIn: false } }, action) => {
    switch(action.type) {
        case USER_LOGIN_REQUEST:
            return { loading: true }
        case USER_LOGIN_SUCCESS:
            return { loading: false, isLoggedIn: action.payload }
        case USER_LOGIN_FAIL:
            return { loading: false, error: action.payload }
        default:
            return state
    }
}

actions.js

export const loginUser = (email, password) => async (dispatch) => {
    const loginUrl = //loginUrl
    try {
        dispatch({ type: USER_LOGIN_REQUEST })
        
        const loginData = { email: email, password: password }
        const config = { headers: { 'Content-type': 'application/json'} }
        const { data } = await axios.post(loginUrl, loginData, config)
        
        dispatch({ type: USER_LOGIN_SUCCESS, payload: true })
        
        storeUserLoggedInDetailsInLocalStorage(data.tokens); //calling a function which stores in localstorage. Nothing else done here.
    } catch(error) {
        dispatch({
            type: USER_LOGIN_FAIL,
            payload: error.response && error.response.data.detail
                    ? error.response.data.detail
                    : error.message
        })
        console.log(error)
    }
}

I have this isLoggedIn boolean value in Login screen which will be set to true once user is logged in. Below is the snippet

const userDetails = useSelector(state => state.userDetails)
const { isLoggedIn } = userDetails

EDIT 2:
store.js

const userDetailsFromStorage = localStorage.getItem('userDetails') ? 
                         JSON.parse(localStorage.getItem('userDetails')) : {}

const reducer = combineReducers({
    userDetails: userLoginReducer,
})

const persistedState = {
    userDetails: {
        isLoggedIn: userDetailsFromStorage.isLoggedIn ?? false
    }
}

const middleWare = [thunk]

const store = createStore(reducer, persistedState, composeWithDevTools(applyMiddleware(...middleWare)));

export default store

答案1

得分: 1

loginUser是一个异步操作,您应该能够使用await来等待它的解析/拒绝。

类似以下示例:

function UserLoginScreen() {
  ...

  const handleLoginClick = async (event) => {
    event.preventDefault();

    try {
      await dispatch(loginUser(email, password));
      navigate('/');
    } catch(error) {
      // 处理或忽略错误/拒绝
    }
  }

  ...
}

演示

使用受保护的路由在React中在按钮点击时导航。

loginUser操作可能需要重新抛出它捕获的任何错误,以便等待的UI可以在UI中捕获并处理它,例如,如果您想要设置一些本地错误状态或触发一个面向用户的提示消息。

英文:

loginUser is an asynchronous action and you should be able to await it to resolve/reject.

Something similar to the following example:

function UserLoginScreen() {
  ...

  const handleLoginClick = async (event) => {
    event.preventDefault();

    try {
      await dispatch(loginUser(email, password));
      navigate('/');
    } catch(error) {
      // handle or ignore errors/rejections
    }
  }

  ...
}

Demo

使用受保护的路由在React中在按钮点击时导航。

The loginUser action may need to re-throw any errors it catches so UI that is waiting can catch and handle it in the UI, for example if you wanted to set some local error state or trigger a toast message to the user.

huangapple
  • 本文由 发表于 2023年6月29日 21:40:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76581594.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定