繁体   English   中英

在 JSX 中断 React 渲染协调时渲染功能组件

[英]Rendering a functional component as JSX breaks React render reconciliation

我希望我的组件的用户能够传入一个组件对象,并且我希望允许他们使用基于函数或类的组件。

type InputComponents = Record<string,  React.ComponentType>

这允许使用 FC 或类组件,但我没有找到有关如何呈现它们的明确指南。

const inputComponents = {
  a: ({ placeholder }) => ( // Functional component
    <input
      type="text"
      value={state.a}
      placeholder={placeholder}
      onChange={(e) => {
        const value = e.currentTarget.value;
        setState((s) => ({ ...s, a: value }));
      }}
    />
  ),
  b: InputClass // Class component
};

两个组件都可以正常渲染,而 Typescript 不会像这样抱怨:

const A = inputComponents.a;
const B = inputComponents.b;

<A placeholder="Type something" />
<B placeholder="Type something" />

但这实际上是一种误导 -功能的 (a) 会在每次按键时失去焦点

使功能组件不会在每次按键时失去焦点的唯一方法是这样的:

inputComponents.a({ placeholder: 'Type something' })

Typescript 甚至不认为这是渲染组件的有效方式,但它是唯一完全有效的方式......它错误地显示“此表达式不可调用”。 并且 Class 组件也失败了,所以我必须这样做:

// Render as JSX for class components and call as a function for FC ones...
Component.prototype.isReactComponent ? <Component placeholder={x} /> : Component({ placeholder: x })

您可以在此处查看实际问题:

 function SomeComponent({ inputComponents }) { const B = inputComponents.b; const C = inputComponents.c; return ( <div className="SomeComponent"> <p>This FC component doesn't loose focus:</p> {inputComponents.a({ placeholder: 'Type something' })} <p>This one does:</p> <B placeholder="Type something" /> <p>Rendering a class component as JSX works though:</p> <C placeholder="Type something" /> </div> ); } class InputClass extends React.Component { state = { value: '' }; render() { return ( <input type="text" value={this.state.value} placeholder={this.props.placeholder} onChange={(e) => { this.setState({ value: e.currentTarget.value }); }} /> ); } } function App() { const [state, setState] = React.useState({ a: '', b: '' }); const inputComponents = { a: ({ placeholder }) => ( <input type="text" value={state.a} placeholder={placeholder} onChange={(e) => { const value = e.currentTarget.value; setState((s) => ({ ...s, a: value })); }} /> ), b: ({ placeholder }) => ( <input type="text" value={state.b} placeholder={placeholder} onChange={(e) => { const value = e.currentTarget.value; setState((s) => ({ ...s, b: value })); }} /> ), c: InputClass }; return ( <div className="App"> <SomeComponent inputComponents={inputComponents} /> </div> ); } ReactDOM.render(<App />, document.getElementById('app'))
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script> <div id="app"></div>

React 坏了吗? 或者在不依赖黑客 TS 错误和使用像isReactComponent这样的内部东西的情况下处理这个问题的正确方法是什么? 谢谢

问题是您在每次重新渲染时都创建了一个新组件(函数本身),因为您在AppinputComponents 如果将类声明移动到内部作用域,类组件也会发生相同的行为

inputComponent = {
  c: class InputClass extends Component {}
}

为了解决这个问题,您可以将组件映射移动到外部作用域并将statesetState作为道具传递。 或者通过上下文提供。

 function SomeComponent({ inputComponents, args }) { const B = inputComponents.b; const C = inputComponents.c; return ( <div className="SomeComponent"> <p>This one does:</p> <B placeholder="Type something" {...args} /> <p>Rendering a class component as JSX works though:</p> <C placeholder="Type something" {...args} /> </div> ); } class InputClass extends React.Component { state = { value: '' }; render() { return ( <input type="text" value={this.state.value} placeholder={this.props.placeholder} onChange={(e) => { this.setState({ value: e.currentTarget.value }); }} /> ); } } const inputComponents = { b: ({ placeholder, state, setState }) => ( <input type="text" value={state.b} placeholder={placeholder} onChange={(e) => { const value = e.currentTarget.value; setState((s) => ({ ...s, b: value })); }} /> ), c: InputClass }; function App() { const [state, setState] = React.useState({ a: '', b: '' }); return ( <div className="App"> <SomeComponent inputComponents={inputComponents} args={{state, setState}} /> </div> ); } ReactDOM.render(<App />, document.getElementById('app'))
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.development.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.development.min.js"></script> <div id="app"></div>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM