REECT:返回JSX和函数组件的函数之间的差异是什么?

发布于 2025-01-17 12:35:44 字数 534 浏览 0 评论 0原文

function renderSomething(foo) {
  return <div>sth {foo}</div>
}

function Something({ foo }) {
  return <div>sth {foo}</div>
}

function Component(props) {
  const { foo } = props
  return (
    <>
        {renderSomething(foo)}
        <Something foo={foo} />
    </>
  )
}

renderSomething() 的 JSX 结果是相同的。我想知道这两种方式之间有什么区别(例如渲染方式、行为、影响等)?

render方法(即renderSomething())适用于什么场景?我可以在里面使用挂钩吗?

function renderSomething(foo) {
  return <div>sth {foo}</div>
}

function Something({ foo }) {
  return <div>sth {foo}</div>
}

function Component(props) {
  const { foo } = props
  return (
    <>
        {renderSomething(foo)}
        <Something foo={foo} />
    </>
  )
}

The JSX result of renderSomething() and <Something /> is identical. I wonder what's the difference(e.g. render way, behavior, influence, etc) between these two ways?

And what applicable scenario for render method(i.e. renderSomething())? Can I use hooks inside?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

伏妖词 2025-01-24 12:35:45

renderSomething() 的 JSX 结果是相同的。

renderSomething(foo) 做完全不同的事情,但在特定用法中,最终结果是相同的:React 创建一个 React 元素,告诉它稍后渲染一个 div (如果使用该元素)。执行 renderSomething(foo) 是将 renderSomething 视为一个钩子,这会影响您在运行时如何使用它。

这里有两个主要区别:

  1. 使用renderSomething(foo)您的代码会立即调用函数并传入参数。对于 ,您并不是在调用 Something。您要求 React 记住它,并在以后需要渲染您执行这些调用的组件时调用它。

  2. 因为调用 renderSomething 的是您的代码,而不是 React,所以如果您在其中使用了钩子,它们会将信息放入调用组件的实例和状态信息中。相反,如果 Something 使用挂钩,则该实例和状态信息将存储在 Something 本身的组件实例中。

(还有其他差异。您不能通过 使用 renderSomething 作为组件,因为函数组件名称必须以大写字母开头以将它们与 HTML 元素区分开来,并且因为 renderSomething 需要一个字符串参数,而不是 props 对象。)

让我们更仔细地看看其中的几个差异:

function renderSomething(foo) {
    console.log(`renderSomething: Called with foo = ${foo}`);
    return <div>sth {foo}</div>;
};

function Something({ foo }) {
    console.log(`Something: Called with foo prop = ${foo}`);
    return <div>sth {foo}</div>;
}

console.log(`Creating elements via renderSomething("x"):`);
const ex1 = renderSomething("x");
console.log(`Creating elements via <Something foo="x" />:`);
const ex2 = <Something foo="x" />;
console.log(`Done`);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

请注意,代码调用 renderSomething(您会看到它的console.log 出现),但没有调用Something (您看不到它的 console.log)。这是因为上面的差异#1。关键是组件的函数只有在需要渲染时才会被调用。我们没有渲染上面的任何结果,因此该函数永远不会被调用。

第二个区别更微妙,但让我们假设两件事:

  1. 你的“东西”需要一个效果(也许它通过网络加载一些东西)(或者它可以是状态或引用或它使用钩子的任何其他东西),并且
  2. 使用它的组件(我们称之为示例)可能需要也可能不需要渲染它。

这与 Something 配合得很好:

const { useState, useEffect } = React;

function Something({ foo }) {
    const [thing, setThing] = useState(null);
    useEffect(() => {
        setTimeout(() => {  // Fake ajax
            setThing("thing"); 
        }, 100);
    }, []);
    return <div>foo = {foo}, thing = {thing}</div>;
}

function App() {
    const [foo, setFoo] = useState("");

    return <div>
        <div>
            <label>
                <input type="checkbox" checked={!!foo} onChange={() => setFoo(foo => foo ? "" : "hi")} />
                Toggle "foo"
            </label>
            {foo ? <Something foo={foo} /> : null}
        </div>
    </div>;
}

ReactDOM.render(<App />, document.getElementById("root"));
label {
    user-select: none;
}
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

但现在让我们用 renderSomething 尝试一下:

const { useState, useEffect } = React;

function renderSomething(foo) {
    const [thing, setThing] = useState(null);
    useEffect(() => {
        setTimeout(() => {  // Fake ajax
            setThing("thing"); 
        }, 100);
    }, []);
    return <div>foo = {foo}, thing = {thing}</div>;
}

function App() {
    const [foo, setFoo] = useState("");

    return <div>
        <div>
            <label>
                <input type="checkbox" checked={!!foo} onChange={() => setFoo(foo => foo ? "" : "hi")} />
                Toggle "foo"
            </label>
            {foo ? renderSomething(foo) : null}
        </div>
    </div>;
}

ReactDOM.render(<App />, document.getElementById("root"));
label {
    user-select: none;
}
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

当您勾选带有此错误的框时,它会爆炸:

警告:React 检测到 App 调用的 Hook 顺序发生了变化。如果不修复,这将导致错误和错误。有关更多信息,请阅读 Hooks 规则:https://reactjs.org/link/rules-of-hooks

   上一个渲染 下一个渲染
   -------------------------------------------------- ----
1. useState 使用状态
2. 未定义的useState
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^

    在应用程序(<匿名>:35:22)

这是因为当我们没有在第一次渲染时调用 renderSomething 时,这会使代码调用它。这是因为上面的#2:由于 renderSomething 不是一个组件函数,它不会获得自己的组件实例,并且对其中的钩子的任何调用就像对父组件中的钩子的调用一样(示例)。但函数组件有时不允许调用钩子,有时则不允许。这是因为 React 依赖于调用 hooks 的顺序,并且该顺序使其执行钩子管理时保持一致。同样,使用 renderSomething 就是使用 renderSomething 作为钩子(这正是自定义钩子的工作原理,最终调用内置钩子,然后将信息存储在父实例)。

正如您所看到的,虽然您在示例中得到的结果是相同的,但总的来说它们是完全不同的东西。 :-)

The JSX result of renderSomething() and <Something /> is identical.

renderSomething(foo) and <Something foo={foo} /> do quite different things, but in that specific usage, the end result is the same: React creates a React element that tells it to render a div later (if the element is used). Doing renderSomething(foo) is treating renderSomething like a hook, which has implications for how you use it when when it runs.

Here are two key differences:

  1. With renderSomething(foo), your code is calling the function immediately and passing in the argument. With <Something foo={foo} />, you aren't calling Something. You're asking React to remember it and call it later if/when it needs to render the component where you did these calls.

  2. Because your code, not React, calls renderSomething, if you used hooks in it, they'd be putting information in the calling component's instance and state information. In contrast, if Something uses hooks, that instance and state information is stored in a component instance for Something itself.

(There are other differences. You can't use renderSomething as a component via <renderSomething foo={foo} /> because function component names must start with upper case to differentiate them from HTML elements, and because renderSomething expects a string argument, not a props object.)

Let's look at a couple of those differences more closely:

function renderSomething(foo) {
    console.log(`renderSomething: Called with foo = ${foo}`);
    return <div>sth {foo}</div>;
};

function Something({ foo }) {
    console.log(`Something: Called with foo prop = ${foo}`);
    return <div>sth {foo}</div>;
}

console.log(`Creating elements via renderSomething("x"):`);
const ex1 = renderSomething("x");
console.log(`Creating elements via <Something foo="x" />:`);
const ex2 = <Something foo="x" />;
console.log(`Done`);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

Notice that the code called renderSomething (you see its console.log occur), but didn't call Something (you don't see its console.log). That's because of difference #1 above. The key thing is that a component's function is called only if it needs to be rendered. We haven't rendered either result above, so the function is never called.

The second difference is more subtle, but let's suppose two things:

  1. Your "something" needs an effect (maybe it loads something via the network) (or it could be state or a ref or anything else that it used a hook for), and
  2. The component using it (let's call it Example) may or may not need to render it.

That works just fine with Something:

const { useState, useEffect } = React;

function Something({ foo }) {
    const [thing, setThing] = useState(null);
    useEffect(() => {
        setTimeout(() => {  // Fake ajax
            setThing("thing"); 
        }, 100);
    }, []);
    return <div>foo = {foo}, thing = {thing}</div>;
}

function App() {
    const [foo, setFoo] = useState("");

    return <div>
        <div>
            <label>
                <input type="checkbox" checked={!!foo} onChange={() => setFoo(foo => foo ? "" : "hi")} />
                Toggle "foo"
            </label>
            {foo ? <Something foo={foo} /> : null}
        </div>
    </div>;
}

ReactDOM.render(<App />, document.getElementById("root"));
label {
    user-select: none;
}
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

But now let's try it with renderSomething:

const { useState, useEffect } = React;

function renderSomething(foo) {
    const [thing, setThing] = useState(null);
    useEffect(() => {
        setTimeout(() => {  // Fake ajax
            setThing("thing"); 
        }, 100);
    }, []);
    return <div>foo = {foo}, thing = {thing}</div>;
}

function App() {
    const [foo, setFoo] = useState("");

    return <div>
        <div>
            <label>
                <input type="checkbox" checked={!!foo} onChange={() => setFoo(foo => foo ? "" : "hi")} />
                Toggle "foo"
            </label>
            {foo ? renderSomething(foo) : null}
        </div>
    </div>;
}

ReactDOM.render(<App />, document.getElementById("root"));
label {
    user-select: none;
}
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

It blows up when you tick the box with this error:

Warning: React has detected a change in the order of Hooks called by App. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks

   Previous render            Next render
   ------------------------------------------------------
1. useState                   useState
2. undefined                  useState
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    at App (<anonymous>:35:22)

That's because that makes the code call renderSomething when we didn't call it on the first render. That's because of #2 above: Since renderSomething isn't a component function, it doesn't get its own component instance, and any calls to hooks within it are just like calls to hooks in the parent component (Example). But function components aren't allowed to call hooks sometimes and not other times. That's because React relies on the order in which hooks are called, and that order has to be consistent for it to do its hook management. Again, using renderSomething like that is using renderSomething as a hook (this is exactly how custom hooks work, by ending up calling built-in hooks, which then store the information in the parent instance).

So as you can see, while the result you got in your example was the same, in general they're quite different things. :-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文