+4 votes
in NextJS by (930 points)
edited by

1 Answer

+1 vote
by (930 points)
  1. Để dễ dàng sử dụng bạn có thể tạo file _app.tsx trong folder pages thay vì sử dụng trực tiếp page.js bên trong folder app. Điều này sẽ giúp bạn tránh một số lỗi outSide và bạn dễ dàng custom web của bạn như thêm Head và Foot cho web.
     
  2. tại file pages/_app.tsx bạn nên bao tất cả bởi một
    <MainLayoutProvider>{your some thing}</MainLayoutProvider>
    Như bạn biết, NextJS hay ReactJS là framework một chiều chỉ truyền từ component cha sang component con. Vì vậy, bạn không thể giao tiếp với các component không liên quan. 

    - Ví dụ: khi bạn xảy ra một event nào đó mà bạn muốn nó tác động đến Header của bạn hay ngược lại. Như bạn biết đó, Header và Footer thông thường được setup độc lập vì vậy chúng không có liên kết với bất kỳ component nào. Vậy  làm thế nào để chúng ta nhận sự thay đổi từ chúng.

    - Giải pháp của tôi chính là MainLayoutProvider. MainLayoutProvider  ở đây sẽ đóng vai trò là một thành phần cha có chức năng tương tự như redux hay store. Nó có thể nhận và export ra các state mà bạn muốn ở bất cứ đâu trên trang web.

    - Hãy tưởng tượng rằng MainLayoutProvider là một nơi trung gian thứ ba khi bạn muốn giao tiếp giữa hai component không liên quan như ví dụ bên trên. Thay vì bạn giao tiếp trực tiếp thì bạn có thể đẩy event, data, ... vào trung gian này và phía bên kia sẽ lấy chúng ra từ trung gian đó. 

    - Dưới đây là ví dụ khởi tạo một Provider

    import React, { useContext } from "react";

    import { useState } from "react";

    interface MainLayoutContextValue {

      isShowCart: boolean;

      setIsShowCart: (value: boolean) => void;

    }

    interface MainLayoutProps {

      children: React.ReactNode;

    }

    const MainLayoutContext = React.createContext<MainLayoutContextValue>({

      isShowCart: false,

      setIsShowCart: () => {},

    });

    const MainLayoutProvider = ({ children }: MainLayoutProps) => {

      const [isShowCart, setIsShowCart] = useState(false);

      const contextValue = React.useMemo(

        () => ({

          isShowCart,

          setIsShowCart,

        }),

        [isShowCart, setIsShowCart]

      );

      return (

        <MainLayoutContext.Provider value={contextValue}>

          {children}

        </MainLayoutContext.Provider>

      );

    };

    const useMainLayoutContext = () => {

      const popups = useContext(MainLayoutContext);

      if (popups == null) {

        throw new Error(

          "useMainLayoutContext() called outside of a PopUpProvider?"

        );

      }

      return popups;

    };

    export { MainLayoutProvider, useMainLayoutContext };


    - và chúng ta có thể lấy và đẩy giá trị isShowCart bằng câu lệnh sau đây ở bất cứ đâu:

    const { isShowCart, setIsShowCart } = useMainLayoutContext();

  3. Bạn không nên import trực tiếp <Header /> vào file _app.tsx. Khi set up tôi đã sử dụng Data Fetching (getStaticProps, getServerSideProps, ...bạn có thể tìm hiểu bài viết cache dữ liệu của tôi) đại khái là chúng sẽ lưu trữ HTML từ server  để render lại page/component bạn muốn. Bởi vì, các menu từ Header của tôi cũng được get từ API vì vậy tôi đã cache chúng. Nhưng khi tôi cập nhật một giá trị trên Header và reload lại lúc này phía server nhận thấy giá trị từ server không khớp so với giá trị hiện tại -> xảy ra lỗi.

    - Tôi đã giải quyết điều này bằng cách tạo một constant dynamic từ Header component như sau:

    const NoSSR = dynamic(() => import("../components/common/Head"), {

      ssr: false,

    });

    và sau đó bạn chỉ cần sử dụng <NoSSR /> thay cho <Header />.

    - Có thể bạn sẽ thắc mắc rằng và các component khác thì có bị ảnh hưởng không? Câu trả lời là KHÔNG, chúng ta không cần thay đổi gì đối với các component đó bởi vì với các component ở mỗi page chúng ta đã có truyền cho chúng một pageProps (<Compoent {...pageProps} />)nó không ở dạng tĩnh như <Header />
     
  4. Bạn nên lưu ý rằng trên trang web của chúng ta không phải bất kỳ page nào cũng sẽ có Header và Footer. Ví dụ như check out page, trang này chúng ta không cần Header và Footer hay thậm chí một số trang bạn cần phải thay đổi các hiển thị của chúng ví dụ tùy điều kiện mà chúng có cần hiển thị Header hay không. Vì vậy, chúng ta sẽ không thể set up trực tiếp mặc định toàn bộ trang sẽ được bao bởi Header và Footer như:
    <>
       <NoSSR />
       <Component {...pageProps} />
       <Footer />
    </>
    - Chúng ta cần hiển thị chúng dưới dạng điều kiện if như sau:
      + bạn khai báo một biến getLayout

    let getLayout =

        Component.getLayout ??

        ((page) => {

          return (

            <>

              <NoSSR />

              {page}

              <Footer />

            </>

          );

        });

     
     + và render chúng:

    <MainLayoutProvider>

            {getLayout(<Component {...pageProps} />)}

     </MainLayoutProvider>


    - Vậy tại những page bạn muốn thay đổi cách hiển thị, chẳng hạn như không hiển thị Header và Footer thì bạn chỉ cần thêm câu lênh:

    Index.getLayout = (page: JSX.Element) => page;


    - Dưới đây là cú pháp cả page:

    import { Checkout } from '../../components/Checkout/index';

    const Index = () => {

      return (

        <Checkout />

      );

    }

    Index.getLayout = (page: JSX.Element) => page;

    export default Index;

Welcome to Qtsd Q&A, where you can ask questions and receive answers from other members of the community.
...