Reputation: 4255
So I want to avoid deep nesting of props and I was starting to use React context to do this, but then it occurred to me "why don't I just export objects instead?"
For example, instead of writing:
const handleClick: = event => {
event.preventDefault();
doSomething();
};
const calcPrice = (quantity) = {
return quantity * 100
};
export const ComponentContext = createContext({});
export const ParentComponent = () => {
return (
<ComponentContext.Provider value={{ handleClick, calcPrice }}>
<ChildComponent quantity={12} />
</ComponentContext.Provider>
}
And import it as:
export const ChildComponent = (quantity) = {
const { handleClick, calcPrice } = useContext(ComponentContext);
const totalPrice = calcPrice(quantity);
return <button onClick={handleClick}>Total is ${totalPrice}</button>
}
I could instead simply write it as:
const handleClick: = event => {
event.preventDefault();
doSomething();
};
const calcPrice = (quantity) = {
return quantity * 100
};
export const componentProps = { handleClick, calcPrice };
export const ParentComponent = () => {
return <ChildComponent quantity={12} />
}
And import it as:
const { handleSignUpClick, calcPrice } = componentProps;
export const ChildComponent = (quantity) = {
const totalPrice = calcPrice(quantity);
return <button onClick={handleClick}>Total is ${totalPrice}</button>
}
What's the advantage of doing it with context instead of with functions?
Upvotes: 2
Views: 1017
Reputation: 31465
It seems that in your example you're just exporting some helper functions. And in that use case, there might not be any difference between exporting an object (with those functions) and using the useContext()
hook.
https://reactjs.org/docs/hooks-reference.html#usecontext
But, from React DOCs (link above), we get that:
A component calling useContext will always re-render when the context value changes. If re-rendering the component is expensive, you can optimize it by using memoization.
How would you achieve that (re-render of the consumers) using an exported object?
From the parent perspective, all that could trigger a re-render of a child component is when you render it with a different props
object. And how would something that you exported that lives outside of the component function (since you can't export local function variables and 'methods') be able to change the props
object that is created inside the component function scope?
TLDR:
The basic difference is that you can't cause a re-render of consumer children with an exported object. At least not without falling into a complete React anti-pattern.
Imagine that you have a ParentComponent
that render two expensive child component, that you want to optmize for. And for that you will use React.memo()
so you only re-render those child components if their props
change.
The one that uses context will re-render, because the context property has changed, but the one that uses the exported variable will not re-render because all that has changed lives outside of React.
SandBox example: https://vq30v.codesandbox.io/
ParentComponent.js
import React, { useState } from "react";
import SomeContext from "./SomeContext";
import ExpensiveChildComponent from "./ExpensiveChildComponent";
import ExpensiveChildComponentExport from "./ExpensiveChildComponentExport";
let count = null; // VARIABLE THAT WILL BE EXPORTED
console.log("Outside ParentComponent...");
function ParentComponent() {
const [myState, setMyState] = useState(0);
console.log("Rendering Parent Component...");
count = myState; // UPDATING THE EXPORTED VARIABLE
function handleClick() {
setMyState(prevState => prevState + 1);
}
// SETTING UP CONTEXT PROVIDER
return (
<div>
<SomeContext.Provider value={myState}>
<button onClick={handleClick}>Count</button>
<h3>Uses Context</h3>
<ExpensiveChildComponent />
<h3>Uses Exported Object</h3>
<ExpensiveChildComponentExport />
</SomeContext.Provider>
</div>
);
}
console.log("After ParentComponent declaration...");
export { ParentComponent, count }; // EXPORTING COMPONENT AND VARIABLE
ExpensiveChildComponent.js (uses context)
import React, { useContext } from "react";
import SomeContext from "./SomeContext";
// REACT MEMO WILL ONLY UPDATE IF PROPS OR CONTEXT HAS CHANGED
const ExpensiveChildComponent = React.memo(function ExpensiveChildComponent() {
console.log("Rendering ExpensiveChildComponent...");
const context = useContext(SomeContext);
return <div>{context}</div>;
});
export default ExpensiveChildComponent;
ExpensiveChildComponentExport.js (uses an exported property)
import React from "react";
import { count } from "./ParentComponent"; // IMPORTING THE EXPORTED VARIABLE
console.log("Outside ExpensiveChildComponentExport...");
// REACT MEMO WILL ONLY UPDATE IF PROPS OR CONTEXT HAS CHANGED (AND BOTH ARE NOT BEING USED)
const ExpensiveChildComponentExport = React.memo(
function ChildComponentExport() {
console.log("Rendering ExpensiveChildComponentExport...");
return (
<React.Fragment>
<div>{count}</div>
</React.Fragment>
);
}
);
export default ExpensiveChildComponentExport;
RESULT:
NOTE:
If you remove React.memo
from the ExpensiveChildComponentExport
it will re-render, because React creates a new props
object on each render (it will be an empty object, but it will be a different one on every time). That's why I added React.memo()
, because it will perform a shallow compare on the props
object. So I can illustrate the behavior that useContext
has and an mere exported object doesn't.
Upvotes: 1