为什么要创建上下文而不是仅导出对象?

时间:2019-05-01 04:56:32

标签: reactjs react-hooks

所以我想避免道具的深层嵌套,并且我开始使用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>
}

使用上下文而不是使用函数有什么好处?

1 个答案:

答案 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;

结果:

enter image description here

注意:

如果您从React.memo中删除ExpensiveChildComponentExport,它将重新渲染,因为React在每个渲染上都创建了一个新的props对象(它将是一个空对象,但将是一个空白对象)。每次都不同)。这就是为什么我添加React.memo()的原因,因为它将对props对象执行浅表比较。因此,我可以举例说明useContext所具有的行为,而仅是导出的对象则没有。