Truyền prop cho mọi trang đơn trong Next.js với getInitialProps bằng cách sử dụng typecript


10

Tôi có một trường hợp khi tôi cần biết người dùng có đăng nhập hay không trước khi trang được hiển thị phía máy chủ và gửi lại cho tôi bằng Next.js để tránh thay đổi nhấp nháy trong giao diện người dùng.

Tôi đã có thể tìm ra cách ngăn người dùng truy cập vào một số trang nếu anh ta đã đăng nhập bằng thành phần HOC này ...

export const noAuthenticatedAllowed = (WrappedComponent: NextPage) => {
    const Wrapper = (props: any) => {
        return <WrappedComponent {...props} />;
    };

    Wrapper.getInitialProps = async (ctx: NextPageContext) => {
        let context = {};
        const { AppToken } = nextCookie(ctx);
        if (AppToken) {
            const decodedToken: MetadataObj = jwt_decode(AppToken);
            const isExpired = () => {
                if (decodedToken.exp < Date.now() / 1000) {
                    return true;
                } else {
                    return false;
                }
            };

            if (ctx.req) {
                if (!isExpired()) {
                    ctx.res && ctx.res.writeHead(302, { Location: "/" });
                    ctx.res && ctx.res.end();
                }
            }

            if (!isExpired()) {
                context = { ...ctx };
                Router.push("/");
            }
        }

        const componentProps =
            WrappedComponent.getInitialProps &&
            (await WrappedComponent.getInitialProps(ctx));

        return { ...componentProps, context };
    };

    return Wrapper;
};

Và điều này làm việc tuyệt vời.

Bây giờ, làm cách nào tôi có thể xây dựng một thành phần HOC tương tự để bọc nó xung quanh, hãy nói "_app.tsx" để tôi có thể chuyển prop "userAuthenticated" cho mỗi trang bằng cách lấy mã thông báo và tìm hiểu xem nó có hết hạn hay không và dựa trên Tôi có thể hiển thị giao diện người dùng phù hợp cho người dùng mà không có hiệu ứng nhấp nháy khó chịu đó không?

Tôi hy vọng bạn có thể giúp tôi với điều đó, tôi đã cố gắng thực hiện giống như cách tôi đã tạo HOC ở trên, nhưng tôi không thể làm điều đó, đặc biệt là Typecript không làm điều này dễ dàng hơn với các lỗi lạ của nó :(


Chỉnh sửa == ==========================================

Tôi đã có thể tạo thành phần HOC như vậy và chuyển pro userAuthenticatedcho từng trang như thế này ...

export const isAuthenticated = (WrappedComponent: NextPage) => {
    const Wrapper = (props: any) => {
        return <WrappedComponent {...props} />;
    };

    Wrapper.getInitialProps = async (ctx: NextPageContext) => {
        let userAuthenticated = false;

        const { AppToken} = nextCookie(ctx);
        if (AppToken) {
            const decodedToken: MetadataObj = jwt_decode(AppToken);
            const isExpired = () => {
                if (decodedToken.exp < Date.now() / 1000) {
                    return true;
                } else {
                    return false;
                }
            };

            if (ctx.req) {
                if (!isExpired()) {
                    // ctx.res && ctx.res.writeHead(302, { Location: "/" });
                    // ctx.res && ctx.res.end();
                    userAuthenticated = true;
                }
            }

            if (!isExpired()) {
                userAuthenticated = true;
            }
        }

        const componentProps =
            WrappedComponent.getInitialProps &&
            (await WrappedComponent.getInitialProps(ctx));

        return { ...componentProps, userAuthenticated };
    };

    return Wrapper;
};

Tuy nhiên, tôi đã phải bọc từng trang với HOC này để chuyển xuống chỗ userAuthenticateddựa cho bố cục toàn cầu mà tôi có, vì tôi không thể bọc thành phần lớp "_app.tsx" với nó, nó luôn gây ra lỗi cho tôi. ..

Những công việc này ...

export default isAuthenticated(Home);
export default isAuthenticated(about);

Nhưng điều này không ...

export default withRedux(configureStore)(isAuthenticated(MyApp));

Vì vậy, có một chút khó chịu khi phải làm điều này cho từng trang và sau đó chuyển xuống chỗ dựa cho bố cục toàn cầu trong mỗi trang thay vì chỉ thực hiện một lần trong "_app.tsx".

Tôi đoán lý do có thể là do "_app.tsx" là thành phần lớp chứ không phải thành phần chức năng như các trang còn lại? Tôi không biết, tôi chỉ đoán thôi.

Bất kỳ trợ giúp với điều đó?

Câu trả lời:


5

Đối với những người bạn có thể gặp phải vấn đề tương tự, tôi có thể giải quyết vấn đề này như sau ...

import React from "react";
import App from "next/app";
import { Store } from "redux";
import { Provider } from "react-redux";
import withRedux from "next-redux-wrapper";
import { ThemeProvider } from "styled-components";
import GlobalLayout from "../components/layout/GlobalLayout";
import { configureStore } from "../store/configureStore";
import { GlobalStyle } from "../styles/global";
import { ToastifyStyle } from "../styles/toastify";
import nextCookie from "next-cookies";
import jwt_decode from "jwt-decode";

 export interface MetadataObj {
   [key: string]: any;
 }

const theme = {
    color1: "#00CC99",
    color2: "#CC0000"
};

export type ThemeType = typeof theme;

interface Iprops {
    store: Store;
    userAuthenticated: boolean;
}

class MyApp extends App<Iprops> {
    // Only uncomment this method if you have blocking data requirements for
    // every single page in your application. This disables the ability to
    // perform automatic static optimization, causing every page in your app to
    // be server-side rendered.

    static async getInitialProps({ Component, ctx }: any) {
        let userAuthenticated = false;

        const { AppToken } = nextCookie(ctx);
        if (AppToken) {
            const decodedToken: MetadataObj = jwt_decode(AppToken);
            const isExpired = () => {
                if (decodedToken.exp < Date.now() / 1000) {
                    return true;
                } else {
                    return false;
                }
            };

            if (ctx.isServer) {
                if (!isExpired()) {
                    userAuthenticated = true;
                }
            }

            if (!isExpired()) {
                userAuthenticated = true;
            }
        }

        return {
            pageProps: Component.getInitialProps
                ? await Component.getInitialProps(ctx)
                : {},
            userAuthenticated: userAuthenticated
        };
    }

    render() {
        const { Component, pageProps, store, userAuthenticated } = this.props;
        return (
            <Provider store={store}>
                <ThemeProvider theme={theme}>
                    <>
                        <GlobalStyle />
                        <ToastifyStyle />
                        <GlobalLayout userAuthenticated={userAuthenticated}>
                            <Component {...pageProps} />
                        </GlobalLayout>
                    </>
                </ThemeProvider>
            </Provider>
        );
    }
}

export default withRedux(configureStore)(MyApp);

Như bạn thấy tôi đã đăng toàn bộ thành phần _app.tsx để bạn có thể thấy các gói mà tôi đang sử dụng.

Tôi đang sử dụng next-redux-wrapperstyled-componentsvới Bản in.

Tôi phải làm appContexttrong gitInitialPropskhi các loại any, nếu không nó sẽ không làm việc. Vì vậy, nếu bạn có một typeđề nghị tốt hơn xin vui lòng cho tôi biết. Tôi đã cố gắng sử dụng loại này NextPageContext, nhưng nó không hoạt động trong trường hợp này vì một số lý do.

Và với giải pháp đó, tôi đã có thể biết liệu người dùng có xác thực hay không và chuyển một chỗ dựa cho bố cục toàn cầu để tôi có thể sử dụng nó trong mỗi trang và không phải thực hiện trên mỗi trang và điều đó cũng có lợi trong trường hợp bạn không muốn tiêu đề và chân trang được hiển thị mỗi lần nếu chúng phải phụ thuộc vào userAuthenticatedprop, bởi vì bây giờ bạn chỉ có thể đặt tiêu đề và chân trang trong GlobalLayoutthành phần và vẫn có chỗ dựa userAuthenticatedtheo ý của bạn: D


Nhận xét của tôi không liên quan đến bài viết của bạn và câu trả lời của bạn. Tôi muốn nói, ReactJStuân theo Lập trình chức năng cũng như OOP, nhưng tôi thấy tư duy phát triển phụ trợ trong mã của bạn với những suy nghĩ OOP. Kế thừa một lớp mới từ một thành phần khác! "Tôi không thấy giống như trước đây.
AmerllicA

1
@AmerllicA Tôi hiểu những gì bạn nói, tôi chỉ thử mọi thứ. Vấn đề là ở SSR, nếu tôi làm việc với "Tạo ứng dụng phản ứng" và thực hiện "Kết xuất phía máy khách" thì tôi sẽ không làm điều đó, nhưng tôi không thể làm cho nó hoạt động theo cách nào khác với SSR, thậm chí là cách được đề xuất bởi Next.js.
Ruby
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.