React 路由器未在导航()上渲染组件

问题描述 投票:0回答:1

成功登录后,我尝试

navigate('/')
,但它无法完全正常工作。我可以看到 url 更新,但
<Home />
(可手动导航)组件未呈现。

不确定此信息是否相关,但这是整个流程。下面是我的

handleSignIn
函数,它正在调用从
handleAuth
钩子导出的
useAuth

  const { isAuthenticating, error, handleAuth } = useAuth()

  const handleSignIn = async (e) => {
    e.preventDefault()
    const success = await handleAuth(signin, emailRef.current.value, passwordRef.current.value)
    if (success) {
      navigate('/')
    }
  }

这个钩子然后发送一个 thunk

来自 useAuth.js

  const { isAuthenticating, error } = useSelector(state => state.user)

  const handleAuth = async (authType, email, password) => {

    try {
      const action = await dispatch(authType({ email, password }))
      if (authType.fulfilled.match(action)) {
        console.log('worked')
        return true;
      }
    } catch (err) {
      console.log(err)
      return false;
    }
  }

signin
重击:

export const signin = createAsyncThunk(
  'user/signin',
  async({ email, password }, { rejectWithValue }) => {
    try {
      const response = await signInWithEmailAndPassword(auth, email, password)
      const accessToken = await response.user.getIdToken()

      return {
        uid: response.user.uid,
        email: response.user.email,
        accessToken
      }
    } catch (error) {
      return rejectWithValue(error.message)
    }
  }
)

然后我在 user-slice.js 中使用

extraReducers
来处理加载和错误状态并更新 redux 存储。这个(
isAuthenticating
error
)是从
useAuth
钩子上拉出来的。

  const initialState = {
    user: null,
    error: {
      type: null,
      message: null
    },
    isAuthenticating: false,
  }
  extraReducers: (builder) => {
    builder
      .addCase(signin.pending, (state) => {
        state.isAuthenticating = true;
        state.error = initialState.error;
      })
      .addCase(signin.fulfilled, (state, action) => {
        state.user = action.payload;
        state.isAuthenticating = false;
        localStorage.setItem('accessToken', action.payload.accessToken);
        localStorage.setItem('email', action.payload.email);
        localStorage.setItem('uid', action.payload.uid);
      })
      .addCase(signin.rejected, (state, action) => {
        state.isAuthenticating = false;
        state.error = handleError(action.payload);
      });
  },

下面是我的路由器路径配置和

PrivateRoute
组件。

  const router = createBrowserRouter([
    {
      path: '/',
      element: <RootLayout />,
      errorElement: <Error />,
      children: [
        {
          index: true,
          element: <PrivateRoute element={<Home />} />,
        },
        {
          path: 'get-started',
          element: <LandingLayout />,
          children: [
            {
              index: true,
              element: <GetStarted />,
            },
            {
              path: 'signup',
              element: <SignUp />
            },
          ]
        }
      ]
    }
  ])

  const PrivateRoute = ({ element }) => {
    const user = useSelector(state => state.user)

    return user ? element : <Navigate to="/get-started" />
  }

RootLayout

const RootLayout = () => {

  return (
    <>
      <Nav />
      <Outlet />
    </>
  )
}

我无法找出是什么原因造成的。任何帮助将不胜感激。

reactjs react-redux react-router react-router-dom
1个回答
0
投票

问题

看起来问题在于

Private
路由查看的是 整个
state.user
对象,而不仅仅是
state.user.user
值,即
null
或 Firebase 用户对象。

store.js:

const store = configureStore({
  reducer: {
    user: userSlice.reducer, // <-- state.user
  },
});

用户切片.js:

const initialState = {
  user: null, // <-- store.user.user
  error: {
    type: null,
    message: null,
  },
  isAuthenticating: false,
};

PrivateRoute.js

const PrivateRoute = ({ element }) => {
  const user = useSelector((state) => state.user);
  console.log(user); // <-- only state.user, e.g. { user , error, isAuthenticating }

  return user ? element : <Navigate to="/get-started" />;
};

PrivateRoute
user
中,always 是一个已定义的对象,因此
element
Home
组件将 always 被渲染。

解决方案建议

更新

PrivateRoute
以引用 nested
state.user.user
属性来了解当前是否有任何经过身份验证的用户。

const PrivateRoute = ({ element }) => {
  const { user } = useSelector((state) => state.user);

  return user ? element : <Navigate to="/get-started" />;
};

我建议还集成

isAuthenticating
状态以在任何身份验证检查正在进行时显示加载指示器。

const PrivateRoute = ({ element }) => {
  const { isAuthenticating, user } = useSelector((state) => state.user);

  if (isAuthenticating) {
    return <LoadingSpinner />;
  }

  return user ? element : <Navigate to="/get-started" />;
};

额外建议

通常建议将

PrivateRoute
组件实现为布局路由组件,这样它就能够保护整组路由而不是一次保护一个路由。

const PrivateRoute = () => {
  const { isAuthenticating, user } = useSelector((state) => state.user);

  if (isAuthenticating) {
    return <LoadingSpinner />;
  }

  return user ? <Outlet /> : <Navigate to="/get-started" />;
};

应用程序.js

const router = createBrowserRouter([
  {
    path: "/",
    element: <RootLayout />,
    children: [
      {
        element: <PrivateRoute />,
        children: [
          { index: true, element: <Home /> },
          // other protected routes
        ],
      }
      {
        path: "get-started",
        element: <AuthLayout />,
        children: [
          { path: "signup", element: <SignUp /> },
        ],
      },
    ],
  },
]);
© www.soinside.com 2019 - 2024. All rights reserved.