所以我想避免道具的深层嵌套,并且我开始使用React上下文来执行此操作,但是后来我想到“为什么我不只是导出对象呢?”
例如,不要写:
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>
}
并将其导入为:
export const ChildComponent = (quantity) = {
const { handleClick, calcPrice } = useContext(ComponentContext);
const totalPrice = calcPrice(quantity);
return <button onClick={handleClick}>Total is ${totalPrice}</button>
}
我可以将其写为:
const handleClick: = event => {
event.preventDefault();
doSomething();
};
const calcPrice = (quantity) = {
return quantity * 100
};
export const componentProps = { handleClick, calcPrice };
export const ParentComponent = () => {
return <ChildComponent quantity={12} />
}
并将其导入为:
const { handleSignUpClick, calcPrice } = componentProps;
export const ChildComponent = (quantity) = {
const totalPrice = calcPrice(quantity);
return <button onClick={handleClick}>Total is ${totalPrice}</button>
}
使用上下文而不是使用函数有什么好处?
答案 0 :(得分:1)
在您的示例中,您似乎只是在导出一些辅助函数。在这种使用情况下,导出对象(具有这些功能)与使用useContext()
钩子之间可能没有任何区别。
https://reactjs.org/docs/hooks-reference.html#usecontext
但是,通过反应文档(上面的链接),我们得到了:
当上下文值更改时,调用useContext的组件将始终重新渲染。如果重新渲染该组件的成本很高,则可以使用备注对其进行优化。
如何使用导出的对象来实现(重新渲染使用者)?
从父级角度看,所有可能触发子部件重新渲染的操作都是在使用其他props
对象进行渲染时。并且,您导出的位于组件函数外部的某些东西(因为您无法导出局部函数变量和“方法”)如何能够更改在组件函数范围内创建的props
对象? / p>
TLDR:
基本区别在于,您不能使用已导出的对象重新渲染消费级子级。至少并非没有陷入一个完整的React反模式。
想象一下,您有一个ParentComponent
可以呈现您想优化的两个昂贵的子组件。为此,您将使用React.memo()
,因此您只需重新如果这些子组件的props
发生变化,则呈现它们。
使用context的将重新呈现,因为context属性已更改,但是使用导出的变量的将不会重新呈现,因为所有已更改的内容都存在于React之外。
沙盒示例: 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 (使用上下文)
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 (使用导出的属性)
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;
结果:
注意:
如果您从React.memo
中删除ExpensiveChildComponentExport
,它将重新渲染,因为React在每个渲染上都创建了一个新的props
对象(它将是一个空对象,但将是一个空白对象)。每次都不同)。这就是为什么我添加React.memo()
的原因,因为它将对props
对象执行浅表比较。因此,我可以举例说明useContext
所具有的行为,而仅是导出的对象则没有。