Usage
This guide will help you integrate next-state-adapter into your Next.js App Router project.
1. Configure Root Store, Store Provider, Typed Hook, and HOC
First, create a root store configuration and set up the necessary hooks and provider.
// ~/store/config.ts
'use client';
import {RootStore} from "@/store/root";
import {createProvider, useStore, withStore as withStoreHoc} from "next-state-adapter";
const makeStore = () => {
return new RootStore()
}
export const useAppStore = useStore.withTypes<RootStore>();
// Hook for hydrating the client store with server data.
export const useAppStoreHydration = useStoreHydration.withTypes<RootStore>()
export const StoreProvider = createProvider(makeStore)
// If you need class component support, create a HOC without 'use client'
// ~/store/withStore.ts
export const withStore = withStoreHoc.withTypes<RootStore>()
2. Wrap Your Components with StoreProvider
Ensure that your application is wrapped in StoreProvider
inside the root layout.
// ~/app/layout.tsx
export default function RootLayout({children}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en">
<body>
<StoreProvider>
{children}
</StoreProvider>
</body>
</html>
);
}
3. Create a Component with Store and Hydrate with Server Data
Here’s an example using MobX, but you can use any state management library.
// ~/todos/list.tsx
'use client';
const TodoList = observer(({initialTodos}: { initialTodos: Todo[] }) => {
const {todos} = useAppStoreHydration((store) => {
// Hydrate the client store with server data
store.todos.init(initialTodos)
})
return (
<ul>
{todos.todos.map((todo) => (
<li id={todo.id} key={todo.id}>
{todo.title}
</li>
))}
</ul>
);
});
TodoList.displayName = 'Todos';
4. Use the Component in a Server Component
// ~/app/todos/page.tsx
export default async function Todos () {
const initialTodos = await fetchTodos(); // Fetch initial data on server side
return <TodoList initialTodos={initialTodos} />;
}
5. Using Class Components
To use next-state-adapter
with class components, use the withStore
HOC:
type Props = {
store: RootStore;
initialUsers: User[];
}
class Users extends Component<Props, {}> {
constructor(props) {
super(props);
}
render () {
const { store } = this.props;
const users = store.users.users;
return (
<div>
{users.map(user => <div key={user.id}>{user.id}</div>)}
</div>
);
}
}
// `withStore` will pass the store as props
export const UsersList = withStore(Users, (store, props) => {
const {initialUsers} = props;
store.users.init(initialUsers);
});
// Server Component
export default async function UsersPage() {
const initialUsers = await fetchUsers();
return <UsersList initialUsers={initialUsers} />;
}