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

Why create contexts instead of just exporting objects?

所以我想避免 props 的深度嵌套,我开始使用 React context 来做到这一点,但后来我想到了 "why don't I just export objects instead?"

例如,而不是写:

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>
}

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

在您的示例中,您似乎只是导出了一些辅助函数。在那个用例中,导出对象(具有这些函数)和使用 useContext() 挂钩之间可能没有任何区别。

https://reactjs.org/docs/hooks-reference.html#usecontext

但是,从 React DOCs(上面的 link),我们得到:

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.

您将如何使用导出的对象实现(re-render 的消费者)?

从父级的角度来看,当您使用不同的 props 对象渲染它时,所有可能触发子组件 re-render 的情况。你导出的位于组件函数之外的东西(因为你不能导出局部函数变量和 'methods')如何能够更改在组件函数范围内创建的 props 对象?

TLDR:

基本区别在于您不能使用导出的对象导致 re-render 个消费者子级。至少不是没有陷入一个完整的 React anti-pattern.


假设您有一个 ParentComponent 渲染 两个昂贵的子组件,您想要对其进行优化。 为此您将使用 React.memo()所以你只有 re-render 那些子组件,如果它们 props 改变了。

使用上下文的会re-render,因为上下文属性变了,但是使用导出变量的不会re-render,因为所有这改变了 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;

结果:

注意:

如果您从 ExpensiveChildComponentExport 中删除 React.memo,它将 re-render,因为 React 在每次渲染时都会创建一个新的 props 对象(它将是一个空对象,但每次都会有所不同)。这就是我添加 React.memo() 的原因,因为它将对 props 对象执行浅比较。因此,我可以说明 useContext 具有的行为,而单纯的导出对象则没有。