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 stage and handleClick via props. This approach broke when props could 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 createContext to generate a context object with a Provider and a Consumer. The provider supplies a value that any descendant can consume.
export const StepperContext = React.createContext();We create a provider component that holds the shared state ( stage) and a method ( handleClick) to update it.
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>
);
}
}Wrap the original Stepper component with StepperProvider so that all nested components can access the context.
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>
);
}
}After introducing the context, the original Stepper no longer needs its own state or prop‑drilling logic. It can be simplified to expose only static sub‑components.
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;Individual sub‑components, such as Stepper.Step, consume the context using the Consumer component.
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>
);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 stage and handleClick without 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
