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

时间:2020-10-22 11:48:50

标签: javascript reactjs

我希望组件的用户能够传递组件的对象,并且希望允许他们使用基于功能或基于类的组件。

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之类的内部内容来处理此问题的正确方法是什么?谢谢

1 个答案:

答案 0 :(得分:2)

问题在于,由于在inputComponents中有App,因此在每个重新渲染上都将创建一个新组件(函数本身)。如果将类声明移至内部作用域,则类组件也会发生相同的行为

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>

相关问题