homeresume
 
   

State management with Next.js and React

May 15, 2022

Global state can be useful when component share some common parts. Also some parts can stay persistent (in local storage) and be used in the next user's session. React provides native way to handle state management using context with the hooks.

Usage

// ...
import { useAppContext } from "context";
import { UPDATE_FEATURE_ACTIVATION } from "context/constants";
export function CustomComponent() {
const { state, dispatch } = useAppContext();
// get value from the store
console.log(state.isFeatureActivated);
// dispatch action to change the state
dispatch({ type: UPDATE_FEATURE_ACTIVATION, payload: { isFeatureActivated: true } });
// ...
}

Context setup

// context/index.jsx
import PropTypes from "prop-types";
import React, {
createContext,
useContext,
useEffect,
useMemo,
useReducer,
} from "react";
import { getItem, setItem, STATE_KEY } from "utils/local-storage";
import { INITIALIZE_STORE } from "./constants";
import { appReducer, initialState } from "./reducer";
const appContext = createContext(initialState);
export function AppWrapper({ children }) {
const [state, dispatch] = useReducer(appReducer, initialState);
const contextValue = useMemo(() => {
return { state, dispatch };
}, [state, dispatch]);
useEffect(() => {
const stateItem = getItem(STATE_KEY);
if (!stateItem) return;
const parsedState = JSON.parse(stateItem);
const updatedState = {
...initialState,
// persistent state
isFeatureActivated: parsedState.isFeatureActivated,
};
dispatch({
type: INITIALIZE_STORE,
payload: updatedState,
});
}, []);
useEffect(() => {
if (state !== initialState) {
setItem(STATE_KEY, JSON.stringify(state));
}
}, [state]);
return (
<appContext.Provider value={contextValue}>{children}</appContext.Provider>
);
}
AppWrapper.propTypes = {
children: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired,
};
export function useAppContext() {
return useContext(appContext);
}

Reducer with actions

// context/reducer.js
import { INITIALIZE_STORE, UPDATE_FEATURE_ACTIVATION } from "./constants";
export const initialState = {
isFeatureActivated: false,
};
export const appReducer = (state, action) => {
switch (action.type) {
case INITIALIZE_STORE: {
return action.payload;
}
case UPDATE_FEATURE_ACTIVATION: {
return {
...state,
isFeatureActivated: action.payload.isFeatureActivated,
};
}
default:
return state;
}
};

Wrapper around the app

// _app.jsx
import { AppWrapper } from "context";
function App({ Component, pageProps }) {
// ...
return (
<AppWrapper>
<Component {...pageProps} />
</AppWrapper>
);
}

Constants

// context/constants.js
export const INITIALIZE_STORE = "INITIALIZE_STORE";
export const UPDATE_FEATURE_ACTIVATION = "UPDATE_FEATURE_ACTIVATION";

Next.js app in production

March 6, 2022

The following things should be considered when Next.js app is running in production

  • Error tracking is crucial in the production environment, proactively fixing the errors leads to a better user experience. Sentry is one of the error tracking services, Next.js app can be easily integrated with it.
npm i @sentry/nextjs
npx @sentry/wizard -i nextjs

Set SENTRY_AUTH_TOKEN environment variable, token can be found at Settings Account API Auth Tokens.

User feedback can be collected via the report dialog.

// sentry.client.config.js
Sentry.init({
// ...
beforeSend(event, hint) {
// Check if it is an exception, and if so, show the report dialog
if (event.exception) {
Sentry.showReportDialog({
eventId: event.event_id,
// other fields can be overridden if they need to be localized
});
}
return event;
},
//...
});
  • Intl API is not supported in all browsers.

  • localStorage API is not available when cookies are blocked.

Integrating Next.js app with Google analytics 4

February 6, 2022

Google analytics helps get more insights into app usage.

Prerequisites

  • Google analytics product is already set up, tracking ID is needed
  • Next.js app should be bootstrapped
  • react-ga4 package is installed

Analytics initialization

Analytics should be initialized inside pages/_app.js file.

import ReactGA from "react-ga4";
// ...
if (isEnvironment("production")) {
ReactGA.initialize(ANALYTICS_TRACKING_ID);
}

Tracking events (clicks, e.g.)

// ...
export function trackEvent(category, label, action = "click") {
if (isEnvironment("production")) {
ReactGA.event({
action,
category,
label,
});
}
}
// ...
<Button onClick={() => trackEvent("category", "label")}>
2021

 

© 2022