reactjs 我能把React元件放在状态里面吗?

nbewdwxp  于 2023-01-30  发布在  React
关注(0)|答案(4)|浏览(140)

如果允许以下三个选项中的任何一个,则找不到任何 * 最近的官方 * 信息?

constructor(props) {
    this.state = {
      item: <SomeItem />,
      item1: () => <SomeItem />,
      item2: SomeItem,
    };
  }

我找到了this答案,但它引用了Web归档中的一个 * 旧链接 *,内容如下:

哪些内容不应进入状态?...React组件:在render()中基于底层属性和状态构建它们。

但是那个链接没有说为什么这是个坏主意,如果它会引入bug,等等。

u59ebvdq

u59ebvdq1#

这是一个很好的问题。
不建议将组件置于状态的原因是,它从根本上违背了React模型,即组件提供了一个呈现方法(这是一个纯函数),React引擎使用该方法自动更新DOM以反映组件的属性和状态值。
渲染器的输出,即React元素,应该由React引擎直接使用。契约是您的应用及其所有组件以纯粹的方式生成一组元素,供React引擎管理。
通过在render中引入副作用,或者将元素置于状态中,你实际上是在破坏“纯”契约,它 * 可能 * 会给予不可预测的结果,这可能会也可能不会被认为是你的应用程序中的bug。bug的细节甚至可能随着React的不同版本、不同的引擎实现而变化。关键是你正在破坏React契约。因此,虽然它可能在某些情况下工作,但在其他情况下也可能不工作,甚至在React本身发生变化的相同情况下也不工作。
React内置了基于prop值缓存渲染的方法,如React.memo,引擎提供并理解这些值,并且是契约的一部分。如果你出于性能原因想缓存渲染输出,这是一种方法。
事实上,这正是React API提供此类函数而不是让您自己执行的原因。

dgtucam1

dgtucam12#

说到底,React组件示例只是对象,而且你可以将对象存储在状态中,所以如果你避免了陷阱,就不会引起任何麻烦。其中一个陷阱是,如果你创建处理程序来放置它们的 prop ,这些处理程序将关闭创建它们的上下文,这可能会导致一些意想不到的结果。下面是一个例子:

const {useState, Fragment} = React;

function Thingy({onClick}) {
    return <div onClick={onClick}>A</div>;
}

// STALE CLOSURE
function Example() {
    const [value, setValue] = useState(0);
    const [comp, setComp] = useState(
        <Thingy onClick={() => { console.log("A: value = " + value); }} />
    );
    
    const handler = () => {
        setValue(v => {
            ++v;
            console.log("B: value = " + v);
            return v;
        });
    };
    
    return <Fragment>
        {comp}
        <div onClick={handler}>B</div>
    </Fragment>;
}

ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>

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

这是典型的陈旧闭包,偶然使用函数组件和钩子(就像我在那里做的那样)而不是类组件可能更容易做到,但使用类组件也绝对是可以做到的。
但是如果您没有这样做(或者没有为您存储的组件创建函数,或者创建的函数不使用它们关闭的任何可能更改的内容),应该也没问题。
但是看看React.memo,这可能是一个更好的答案,取决于您希望将组件示例置于状态中的原因是什么。

2j4z5cfb

2j4z5cfb3#

如果我没理解错的话,你可以做这样的事

const Title = () => {
  return <h1>Hello CodeSandbox</h1>;
};

class App extends React.Component {
  state = {}
  constructor(props) {
    super(props)
    this.state = {
      item: function() {
        return <Title />;
      }
    };
  }
  render() {
    return (
      <div className="App">
        {this.state.item()}
        <h2>Start editing to see some magic happen!</h2>
      </div>
    );
  }
}

export default App;
e7arh2l6

e7arh2l64#

你可以这么做,但是有点奇怪,React元素和其他元素一样是一个对象,在这个例子中,它是一个方法调用的结果:

// `<SomeItem/>` compiles to 
React.createElement(SomeItem, null);
// A similar `React.createElement("div", null)` becomes
const el = {
  $$typeof: Symbol(react.element),
  key: null,
  props: {},
  ref: null,
  type: "div",
  _owner: null,
}

这很奇怪,因为它是不必要的(而且有点混乱),你可以在需要的时候生成元素(包括任何状态或属性更新)。
还有一个风险是,你可能会打破React的核心保证之一:元素是不可变的。2这样存储元素会给你一个改变它的机会,从而混淆React。
如果您需要同一个元素的多个副本,那么这样保存它可能会稍微提高性能,特别是在生成它的开销很大的情况下。

相关问题