【问题标题】:Does passing an object via props to child react component, clone the original object or pass by reference?是通过道具将对象传递给子反应组件,克隆原始对象还是通过引用传递?
【发布时间】:2026-02-20 19:40:01
【问题描述】:

如果 am 通过 components props 将对象传递给子组件,这个对象是被克隆还是只是传递对原始对象的引用?

例如,在我的App.js 中,我正在导入一个 JSON 对象 ENTRY_DATA。然后我通过道具将该对象传递给我的子组件(或者在这种情况下,路由)。这样做是在节省内存,还是与在每个组件上导入 ENTRY_DATA 一样?

import React, { Component } from 'react';
import { withRouter, Route } from 'react-router-dom'
import ENTRY_DATA from './../../entry_data.json';


import Register from '../Register/Register';
import Categories from '../Categories/Categories';
import Category from '../Category/Category';
import Entry from '../Entry/Entry';

class App extends Component {

  render() {

    return (
      <div className="app" >

        <main>
          <Route exact path="/" component={Register} />
          <Route exact path='/categories' render={(props) => (
            <Categories {...props} categories={ENTRY_DATA} />
          )}/>
          <Route exact path='/categories/:category' render={(props) => (
            <Category {...props} categories={ENTRY_DATA} />
          )}/>
          <Route exact path='/categories/:category/:entry' render={(props) => (
            <Entry {...props} categories={ENTRY_DATA} />
          )}/>
        </main>

      </div>
    );
  }
}

export default withRouter(App);

如果ENTRY_DATA 是 5kb,并且我将它传递给 3 个不同的组件,这是否意味着我最终得到了 20kb 的 ENTRY_DATA 或者它们都引用了一个 5kb ENTRY_DATA

【问题讨论】:

  • 参考。这使得它可能是可变的。
  • @DimitarChristoff 你有任何消息来源可以证实这一点吗?你说可能是可变的。你的意思是因为理论上它可能会发生变异,但因为 react 使得 this.props 是只读的?
  • 可能是this的副本
  • 它可能是可变的,因为它会被转换为您的目标 - 拥有不可写的对象取决于属性描述符和 polyfills 的实现(在旧 IE 中也是可能的)。但如果您传递的是 JSON 结构(又名对象),您的组件可能会覆盖结构深处的对象值并导致副作用。你应该考虑 Immutable.js 或类似的。
  • 是的,如果它是一个对象,它会传递引用。

标签: javascript reactjs


【解决方案1】:

通常,这取决于所述道具的数据类型。原语,例如 integersStrings 是通过它们的值传递的,而 Object 数据类型比如 arrays 是通过它们的引用传递的。

所以是的,对象具体是通过引用传递下来的。


演示:

在这个演示中,我们有两个组件,一个父级和一个子级。孩子拿了两个道具,一个是整数1,另一个是a: "foo"的对象。

过了一会儿,我们从父对象中将整数值从1 更改为2,并将对象从a: "foo" 更改为a: "bar"。之后,我们从子组件记录这些 props 的值。
注意:这是一个无状态组件,因此我们不使用任何类型的状态.我们直接改变了值,所以 React 永远不会“知道”这个改变! (ssh,别告诉 React!)

整数的输出仍然是1(没有改变 - 通过值传递),但对象的输出是a: "bar"(改变了! - 通过引用传递)。

const Parent = () => { 
  let myInt = 1;
  let myObj = {a: "foo"};
  setTimeout(function(){
    myInt = 2;
    myObj.a = "bar";
  }, 500);
 
  return <Child myInt={myInt} myObj={myObj} />;
}

const Child = ({myInt, myObj}) => {
  setTimeout(function(){
    console.log(myInt);
    console.log(myObj);
  }, 1000);
  
  return (
    <div>
      <p>{myInt}</p>
      <p>{JSON.stringify(myObj)}</p>
    </div>
  );
}
 
ReactDOM.render(<Parent />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

【讨论】:

  • 这是一个很棒的演示,彻底回答了我的问题。谢谢@Chris
  • 很高兴它有帮助:)
【解决方案2】:

Props 对对象的引用,实际上你可以通过上下文传递方法。变异 props 是一种非常糟糕的做法,几乎每个 linter 都不鼓励,当你想要变异来自 props 的数据时,最好深入克隆 prop 并处理克隆数据。

【讨论】:

    【解决方案3】:

    仅传递对对象的引用。事实上,jsx/tsx 和普通的旧 js/ts 之间的转换非常简单。 This babel reference 就是一个很好的例子。

    这意味着可以发送任何类型的对象 - 数字、布尔值、数组、函数、普通旧对象、DOM 元素等。这也意味着如果对象是可变的,它可以被修改。但它不应该。

    【讨论】: