簡體   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