Andrea Gandino

Using Context in Preact/React

Learn how to use the Context in Preact or React to create a globally available application state.

Published on Preact, React, Programming

In Preact and React, a Context is a globally available state that is shared throughout your entire application, which means that your components can access its data wherever they reside, allowing you to avoid passing such data as props to a Component and its children, recursively.

To create a global set of data, you can use the createContext function; in this specific example, the goal is to create a simple Log object, that components can access to add their own messages to its stack.

import { createContext } from 'preact';

const LoggerContext = createContext( null );

As you can see, the createContext function works in a similar way than the useRef hook, in which you initially set it to a null value, and then assign it to its correct object later in the code.

For a Context to work, it needs to be "provided" to your application: to do so, you need to create a provider component, which could wrap others in order to make its data available to them.

The provider component is nothing else than a regular component that has its own internal state with its name depending on the name of the previously created Context object, having .Provider appended.

import { useState } from 'preact/hooks';

export const LoggerProvider = ( props ) => {
	const [ messages, setMessages ] = useState( [] );

	const logMessage = ( msg, type ) => {
		setMessages( ( prevMessages ) => [ { message: msg, type: type }, ...prevMessages ] );
	};

	const Log = {
		info: (msg) => logMessage(msg, 'info'),
		success: (msg) => logMessage(msg, 'success'),
		error: (msg) => logMessage(msg, 'error'),
	}

	return (
		<LoggerContext.Provider value={ {
			messages,
			clear: () => { setMessages( [] ); },
			info: Log.info,
			error: Log.error,
			success: Log.success
		} }>
			{ props.children }
		</LoggerContext.Provider>
	);
};

The last thing to create is a method that would allow internal components to access the context data. We can do so by declaring and exporting an helper function that internally uses the useContext hook, again referencing the Context object we have created initially:

import { useContext } from 'preact/hooks';

export const useLogger = () => useContext( LoggerContext );

At this point, we're pretty much set up for closing the job. In your application entry point, make use of the provider object and wrap the sub-components you want to have access to the logger: in the following case, I want the entire application to potentially log messages.

import LoggerProvider from './path/to/the/context/file';

const App = () => {
	return (
		<LoggerProvider>
			<YourComponent />
			<AnotherComponent />
		</LoggerProvider>
	);
};

The individual sub-components, will be able to access the logger like so:

import { useLogger } from './path/to/the/context/file';

export default function( props ) {
	const Log = useLogger();

	// E.g. Log.info( "Log entry" )
}