javascript - 在 JSX 中渲染功能组件会破坏 React 渲染协调
问题描述
我希望我的组件的用户能够传入组件对象,并且我希望允许他们使用基于函数或类的组件。
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
?谢谢
解决方案
问题是您在每次重新渲染时都创建了一个新组件(函数本身),因为您inputComponents
在App
. 如果将类声明移动到内部范围,类组件也会发生相同的行为
inputComponent = {
c: class InputClass extends Component {}
}
要解决此问题,您可以将组件映射移动到外部范围并传递state
和setState
作为道具。或通过上下文提供。
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>
推荐阅读
- python - 使用熊猫根据工作表中的数据命名excel工作表
- javascript - 按钮 ID 问题
- angular - 来自数据库的角度 8 动态路由
- react-native - ReactNative ScrollView 在不同平台上无法正常工作
- opengl - 如何制作范围有限的法线贴图着色器?
- pygame - 如何在pygame中设置图像的位置?
- python - Python试图更新二维numpy数组中的值,值不更新
- c# - WinSCP 给出我的主机密钥指纹不正确但随机使用相同指纹的错误
- c++ - 在静态链接的二进制文件中获取进程内回溯
- c# - Xamarin.Forms Android 的自定义渲染器(条目)中的 OnElementChanged 仅被调用一次