How to Use React Context API to Build Flexible, Reusable Components
This tutorial demonstrates how to replace fragile prop‑drilling in a multi‑step React component by leveraging the React Context API, showing step‑by‑step creation of a context, provider, and consumer, refactoring the Stepper component for greater flexibility and reusability across the UI.
Original article: https://itnext.io/using-advanced-design-patterns-to-create-flexible-and-reusable-react-components-part-2-react-3c5662b997ab
In the previous part we used compound components and static class methods to create flexible, reusable components, passing
stageand
handleClickvia props. This approach broke when
propscould only be passed to direct children, making the API rigid.
The solution is to use the React Context API, which allows data sharing across the component tree without explicit prop‑drilling.
React Context API
React provides
createContextto generate a context object with a
Providerand a
Consumer. The provider supplies a value that any descendant can consume.
<code>export const StepperContext = React.createContext();</code>We create a provider component that holds the shared state (
stage) and a method (
handleClick) to update it.
<code>class StepperProvider extends Component {
state = { stage: 1 };
render() {
return (
<StepperContext.Provider value={{
stage: this.state.stage,
handleClick: () => this.setState({ stage: this.state.stage + 1 })
}}>
{this.props.children}
</StepperContext.Provider>
);
}
}</code>Wrap the original
Steppercomponent with
StepperProviderso that all nested components can access the context.
<code>class App extends Component {
render() {
return (
<StepperProvider>
<Stepper stage={1}>
<Stepper.Progress>
<Stepper.Stage num={1} />
</Stepper.Progress>
<Stepper.Steps>
<Stepper.Step num={1} text="Stage 1" />
</Stepper.Steps>
</Stepper>
</StepperProvider>
);
}
}</code>After introducing the context, the original
Stepperno longer needs its own state or prop‑drilling logic. It can be simplified to expose only static sub‑components.
<code>class Stepper extends Component {
static Progress = Progress;
static Steps = Steps;
static Stage = Stage;
static Step = Step;
render() {
return <div>{this.props.children}</div>;
}
}
export default Stepper;</code>Individual sub‑components, such as
Stepper.Step, consume the context using the
Consumercomponent.
<code>export const Step = ({ num, text }) => (
<StepperContext.Consumer>
{value => {
const { stage } = value;
return stage === num ? <div key={num} style={styles.stageContent}>{text}</div> : null;
}}
</StepperContext.Consumer>
);</code>This pattern, often called “render props”, lets components subscribe to context values without being direct children of the provider.
By refactoring with Context, the component hierarchy becomes more flexible: any component can access
stageand
handleClickwithout strict parent‑child relationships, enabling the addition of new UI elements such as headers.
Images illustrating the component structure and context flow:
The next part of the series will explore using render props to achieve the same flexibility without relying on Context for state sharing.
Tencent IMWeb Frontend Team
IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.