113
loading...
This website collects cookies to deliver better user experience
Component
- page component (pages/index.jsx)Next-server
- next.js application host_app
- next.js app component (pages/_app.jsx)API-server
- backend-application (not provided in demo, but in real world will be)components
- Folder with all app components (exclude pages)BoookCard
pages
(every folder is separate app route and page. Also the service components (_app, _document) are stored here)_app
index
(main page)store
(mobx store)Books
(BooksStore)index
(useStore hook and initialize store methods)utils
(mock datas and other utils)index
import { makeAutoObservable } from "mobx";
import { books, clientBooks } from "../utils";
class BooksStore {
constructor() {
// define and init observables
this.books = [];
this.searchParam = "";
(make all object properties observables, getters computed, methods actions)
makeAutoObservable(this);
}
setSearchParam = (param) => {
this.searchParam = param;
};
setBooks = (books) => (this.books = books);
get filteredBooks() {
return this.books.filter((book) =>
book.title.toLowerCase().includes(this.searchParam.toLowerCase())
);
}
get totalBooks() {
return this.books.length;
}
// if data is provided set this data to BooksStore
hydrate = (data) => {
if (!data) return;
this.setBooks(data.books);
};
// special method for demonstration
fetchAndSetBooksOnClient = async () => {
const newBooks = await Promise.resolve([...books, ...clientBooks]);
console.log(newBooks);
this.setBooks(newBooks);
};
}
export default BooksStore
store/index.js
file:// we need to enable static rendering for prevent rerender on server side and leaking memory
import { enableStaticRendering } from "mobx-react-lite";
import BooksStore from '../BooksStore'
// enable static rendering ONLY on server
enableStaticRendering(typeof window === "untdefined")
// init a client store that we will send to client (one store for client)
let clientStore
const initStore = (initData) => {
// check if we already declare store (client Store), otherwise create one
const store = clientStore ?? new BooksStore();
// hydrate to store if receive initial data
if (initData) store.hydrate(initData)
// Create a store on every server request
if (typeof window === "undefined") return store
// Otherwise it's client, remember this store and return
if (!clientStore) clientStore = store;
return store
}
// Hoook for using store
export function useStore(initData) {
return initStore(initData)
}
import { useStore } from "../store";
import { createContext } from "react";
import { getSide } from "../utils";
export const MobxContext = createContext();
const MyApp = (props) => {
console.log("hello from _app - ", getSide());
const { Component, pageProps, err } = props;
const store = useStore(pageProps.initialState);
return (
<MobxContext.Provider value={store}>
<Component {...pageProps} err={err} />
</MobxContext.Provider>
);
};
export default MyApp;
import { getSide, books } from "../utils";
import { useContext } from "react";
import { MobxContext } from "./_app";
import BookCard from "../components/BookCard";
import { observer } from "mobx-react-lite";
const IndexPage = () => {
const {
totalBooks,
filteredBooks,
setSearchParam,
fetchAndSetBooksOnClient
} = useContext(MobxContext);
console.log("hello from Page component ", getSide());
const handleOnInputChange = (e) => {
setSearchParam(e.target.value);
};
return (
<div>
<h1>Books:</h1>
<h3>TotalBooks: {totalBooks}</h3>
<button onClick={fetchAndSetBooksOnClient}>Fetch on Client</button>
<input placeholder="search" type="text" onChange={handleOnInputChange} />
<hr />
<div style={{ display: "flex" }}>
{filteredBooks.map((book, index) => (
<BookCard key={index} book={book} />
))}
</div>
</div>
);
};
export const getServerSideProps = async () => {
console.log("making server request before app", getSide());
// here could be any async request for fetching data
// const books = BooksAgent.getAll();
return {
props: {
initialState: {
booksStore: {
books
}
}
}
};
};
export default observer(IndexPage);