【问题标题】:Why create contexts instead of just exporting objects?为什么要创建上下文而不仅仅是导出对象?
【发布时间】:2019-05-01 04:56:31
【问题描述】:

所以我想避免 props 的深度嵌套,我开始使用 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>
}

使用上下文而不是函数的优势是什么?

【问题讨论】:

  • doSomething() in handleClick 需要做什么?这似乎不太可能对您的应用程序状态没有某种依赖性。如果handleClick 对 state 或 props 没有任何依赖,那么就没有理由使用 context。
  • 它涉及更改状态(将 dialogOpen 状态从 false 更改为 true,以便打开对话框表单)。我现在实际上正在使用自定义反应挂钩来执行此操作,而不是上下文或对象,因为 handleClick() 需要包含一个挂钩本身。

标签: reactjs react-hooks


【解决方案1】:

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

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

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

调用 useContext 的组件总是会在上下文值发生变化时重新渲染。如果重新渲染组件的开销很大,您可以使用 memoization 对其进行优化。

您将如何使用导出的对象来实现(重新渲染消费者)?

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

TLDR:

基本区别在于,您不能使用导出的对象重新渲染消费者子级。至少不会落入一个完整的 React 反模式。


假设您有一个 ParentComponent 来渲染 您想要优化的两个昂贵的子组件。 为此,您将使用 React.memo(),因此您只需重新渲染那些子组件如果他们的props 发生变化。

使用上下文的那个会重新渲染,因为上下文属性已经改变了,但是使用导出变量的那个不会重新渲染,因为所有已经改变的东西都存在于 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,它将重新渲染,因为React 在每次渲染时都会创建一个新的props 对象(它将是一个空对象,但每次都会是不同的对象)。这就是我添加React.memo() 的原因,因为它会对props 对象执行浅比较。所以我可以说明useContext 具有的行为,而仅仅导出的对象则没有。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-27
    • 1970-01-01
    • 2021-03-21
    • 1970-01-01
    • 1970-01-01
    • 2012-08-26
    相关资源
    最近更新 更多