[英]React-Loadable re-rendering causing input to lose focus
我遇到一个问题,其中react-loadable
导致状态更新后我的输入组件之一重新呈现并失去焦点。 我已经进行了一些挖掘,但找不到其他人遇到此问题,因此我认为我在这里遗漏了一些东西。
我试图根据用户选择的主题,使用react-loadable
将组件动态包含到我的应用程序中。 一切正常。
./components/App
import React from 'react';
import Loadable from 'react-loadable';
/**
* Import Containers
*/
import AdminBar from '../../containers/AdminBar';
import AdminPanel from '../../components/AdminPanel';
import 'bootstrap/dist/css/bootstrap.css';
import './styles.css';
const App = ({ isAdmin, inEditMode, theme }) => {
const MainContent = Loadable({
loader: () => import('../../themes/' + theme.name + '/components/MainContent'),
loading: () => (<div>Loading...</div>)
});
const Header = Loadable({
loader: () => import('../../themes/' + theme.name + '/components/Header'),
loading: () => (<div>Loading...</div>)
});
return (
<div>
{
(isAdmin) ? <AdminBar
className='admin-bar'
inEditMode={inEditMode} /> : ''
}
<Header
themeSettings={theme.settings.Header} />
<div className='container-fluid'>
<div className='row'>
{
(isAdmin && inEditMode) ? <AdminPanel
className='admin-panel'
theme={theme} /> : ''
}
<MainContent
inEditMode={inEditMode} />
</div>
</div>
</div>
);
};
export default App;
./components/AdminPanel
import React from 'react';
import Loadable from 'react-loadable';
import './styles.css';
const AdminPanel = ({ theme }) => {
const ThemedSideBar = Loadable({
loader: () => import('../../themes/' + theme.name + '/components/SideBar'),
loading: () => null
});
return (
<div className='col-sm-3 col-md-2 sidebar'>
<ThemedSideBar
settings={theme.settings} />
</div>
);
};
export default AdminPanel;
这是我的<ThemedSideBar />
组件的样子:
./themes/Default/components/SideBar
import React from 'react';
import ThemeSettingPanel from '../../../../components/ThemeSettingPanel';
import ThemeSetting from '../../../../containers/ThemeSetting';
import './styles.css';
const SideBar = ({ settings }) => {
return (
<ThemeSettingPanel
name='Header'>
<ThemeSetting
name='Background Color'
setting={settings.Header}
type='text'
parent='Header' />
<ThemeSetting
name='Height'
setting={settings.Header}
type='text'
parent='Header' />
</ThemeSettingPanel>
);
};
export default SideBar;
./components/ThemeSettingPanel
import React from 'react';
import { PanelGroup, Panel } from 'react-bootstrap';
const ThemeSettingPanel = ({ name, children }) => {
return (
<PanelGroup accordion id='sidebar-accordion-panelGroup'>
<Panel>
<Panel.Heading>
<Panel.Title toggle>{name}</Panel.Title>
</Panel.Heading>
<Panel.Body collapsible>
{children}
</Panel.Body>
</Panel>
</PanelGroup>
);
};
export default ThemeSettingPanel;
./containers/ThemeSetting
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { themeSettingChange } from '../App/actions';
import ThemeSetting from '../../components/ThemeSetting';
class ThemeSettingContainer extends Component {
constructor(props) {
super(props);
this.handleOnChange = this.handleOnChange.bind(this);
}
handleOnChange(name, parent, value) {
const payload = {
name: name,
parent,
value: value
};
this.props.themeSettingChange(payload);
}
render() {
return (
<ThemeSetting
name={this.props.name}
setting={this.props.setting}
parent={this.props.parent}
type={this.props.type}
handleOnChange={this.handleOnChange} />
);
}
}
//----Redux Mappings----//
const mapStateToProps = (state) => ({
});
const mapDispatchToProps = {
themeSettingChange: (value) => themeSettingChange(value)
};
export default connect(mapStateToProps, mapDispatchToProps)(ThemeSettingContainer);
./component/ThemeSetting
import React from 'react';
import TextField from '../common/TextField';
import './styles.css';
const ThemeSetting = ({ name, setting, type, parent, handleOnChange }) => {
return (
<div className='row theme-setting'>
<div className='col-xs-7'>
{name}
</div>
<div className='col-xs-5'>
{
generateField(type, setting, name, parent, handleOnChange)
}
</div>
</div>
);
};
function generateField(type, setting, name, parent, handleOnChange) {
const value = setting ? setting[name] : '';
switch (type) {
case 'text':
return <TextField
value={value}
name={name}
parent={parent}
handleOnChange={handleOnChange} />;
default:
break;
}
}
export default ThemeSetting;
./components/common/TextField
import React from 'react';
import { FormControl } from 'react-bootstrap';
const TextField = ({ value, name, parent, handleOnChange }) => {
return (
<FormControl
type='text'
value={value}
onChange={(e) => {
handleOnChange(name, parent, e.target.value);
}} />
);
};
export default TextField;
当我的管理面板中的字段更新时,将触发状态更改。 似乎这触发了react-loadable
以重新渲染我的<ThemedSideBar />
组件,这破坏了我的输入并使用更新后的值创建了一个新的输入。 其他人遇到过这个问题吗? 有没有办法阻止react-loadable
重新渲染?
编辑: 这是请求的回购链接。
编辑:根据评论中的谈话,我很抱歉,我读错了问题。 此处的答案已更新(原始答案在更新的答案下方)
通过查看react-loadable文档 ,看来Loadable HOC打算在render
方法之外调用。 在您的情况下,您正在ThemedSideBar
的render方法中AdminPanel
。 我怀疑您对TextEdit
的输入所做的更改(已通过更新以更新您的Redux状态,然后又通过组件链传递回来)导致React考虑重新呈现AdminPanel
。 因为对Loadable
的调用在render
方法内部(即AdminPanel
是一个表示性组件),所以每当React到达该代码路径时, react-loadable
呈现一个全新的已加载组件。 因此,React认为它需要销毁先前的组件,以使这些组件与新的道具同步更新。
这有效:
import React from 'react'; import Loadable from 'react-loadable'; import './styles.css'; const ThemedSideBar = Loadable({ loader: () => import('../../themes/Default/components/SideBar'), loading: () => null }); const AdminPanel = ({ theme }) => { return ( <div className='col-sm-3 col-md-2 sidebar'> <ThemedSideBar settings={theme.settings} /> </div> ); }; export default AdminPanel;
看来您的问题可能与您建立TextField
的方式有关,而不是react-loadable
。
FormControl
value={value}
和onChange
处理程序作为道具。 这意味着您已经表明它是受控的(而不是不受控制的 )组件。
如果要在用户键入输入时使字段采用更新后的值,则需要传播onChange
处理程序捕获的更改,并确保将其反馈给value={value}
属性中的value={value}
。
现在,看起来value
将始终等于theme.settings.Height
等(大概为null / empty)。
一种替代方法是使FormControl成为不受控制的组件,但我猜您不想这样做。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.