我正在使用React和Draft.js处理Rich Text Editor组件,该组件将成为我的团队用来构建应用程序UI的库的一部分。我遇到了组织组件以分离状态和功能的问题。我不确定这对我的问题是否重要,但我们在我们的应用中使用Redux,但在我们的库中没有。
这是我简化的代码版本,试图关注我遇到的问题:
class TextInput extends React.Component {
constructor(props) {
super(props);
this.state = {
editorState: this.getInitialEditorState()
};
this.onChange = this.onChange.bind(this);
this.toggleLinkMenu = this.toggleLinkMenu.bind(this);
this.toggleInline = this.toggleInline.bind(this);
this.toggleBlock = this.toggleBlock.bind(this);
}
onChange(editorState) {
this.setState({ editorState });
}
toggleLinkMenu() {
this.setState({ displayLinkTooltip: !this.state.displayLinkTooltip });
}
toggleInline(style) {
this.setState({
editorState: RichUtils.toggleInlineStyle(this.state.editorState, this.options.style)
},
this.editor.focus
);
}
toggleBlock(blockType) {
this.setState({
editorState: RichUtils.toggleBlockType(this.state.editorState, this.options.blockType)
},
this.editor.focus
);
}
render() {
// While I'm developing, I have TextInput hardcoded to display every button in TextInputMenu:
const buttonList = ['bold', 'italic', 'underline', 'hyperlink', 'headline', 'blockquote'];
return (
<div className={styles.textInput} >
<TextInputMenu buttonList={buttonList} toggleInline={this.toggleInline} toggleBlock={this.toggleBlock} />
<Editor // This is the actual Draft.js Editor component where the user types their text
ref={c => (this.editor = c)}
editorState={this.props.editorState}
onChange={this.props.onChange}
placeholder={this.props.placeholder}
/>
</div>
);
}
}
// TextInputMenu */
// Contains a series of buttons that the user can click on to apply an inline
// style, apply a block style, or insert a hyperlink.
//
// The TextInput passes in a buttonList prop containing the names of the buttons
// that it wants to display.
class TextInputMenu extends React.Component {
constructor(props) {
super(props);
this.menuButtons = {
bold: { name: 'Bold', icon: 'fa-bold', options: /* Options here */ },
italic: { name: 'Italic', icon: 'fa-italic', options: /* Options here */ },
underline: { name: 'Underline', icon: 'fa-underline', options: /* Options here */ },
hyperlink: { name: 'Hyperlink', icon: 'fa-link', options: /* Options here */ },
headline: { name: 'Headline', icon: 'fa-header', options: /* Options here */ },
blockquote: { name: 'Block Quote', icon: 'fa-quote-left', options: /* Options here */ }
};
}
getMenuButtons() {
return _.pick(this.menuButtons, this.props.buttonList);
}
render() {
return (
<div className={styles.textInputMenu}>
{_.map(this.getMenuButtons(), ({name, icon, func}) => <TextInputMenuButton key={name} style={name} icon={icon} onClick={func} />)}
{this.getMenuButtons().find('hyperlink') && <LinkMenu />}
</div>
);
}
}
const TextInputMenuButton = ({style, icon, onClick}) => (
<div className={styles.textInputButton}>
<i onClick={() => onClick(style)} className={`fa ${icon}`} />
</div>
);
const LinkMenu = ({active, insertHyperlink}) => (
<Tooltip active={active}>
<form onSubmit={insertHyperlink}>
<label>URL:</label>
<input type="text" name="url" />
<label>Displayed Text:</label>
<input type="text" name="displayText" />
<Button />
</form>
</Tooltip>
);
export { TextInput };
export default TextInput;
一般来说,我避免使用refs;但是,我需要调用编辑器组件&#34; s#34;焦点&#34;方法。除了使用ref之外,还有更好的方法来调用子组件的方法吗?
我在TextInput中定位editorState,因为它的子组件使用它。但是,这要求我找到在TextInput中改变editorState的方法,即使在子组件中定位它们更有意义。例如,我想将处理TextInputMenu按钮(toggleLinkMenu,toggleInline和toggleBlock)行为的方法移动到菜单本身,因为随着我添加更多按钮,传递配置变得越来越难以处理并处理PropTypes。有一些替代模式吗?我知道React生态系统倾向于支持函数式编程技术,但是从面向对象编程中获取一个页面并将editorState的setter函数传递给Menu似乎更好。
答案 0 :(得分:0)
Is there a better way to call a child component's methods other than using a ref
我不知道更好,但如果您不想使用ref,那么您可以重新渲染父级(例如,使用setState)将支柱传给孩子 - 例如提升状态 - 然后您可以在孩子的componentDidUpdate
方法中使用。将之前的道具与当前道具进行比较,如果您传递的道具不同,则调用该方法。
答案 1 :(得分:0)
Draft.js有一个类似你所描述的setter函数:onChange
。您将其传递给Draft.js <Editor>
,并且也可以将其传递给<TextInputMenu>
。扩展Draft.js编辑器时,这是一种相当常见的模式。
对于您的focus
问题,如果您尚未阅读,请查看official documentation,了解为何必须对其进行管理。
查看您的示例,您也可以通过将工具栏按钮与onMouseDown
而不是onClick
绑定来解决此问题 - 这样,在事件中使用preventDefault()
时,就不会有单击按钮时焦点转移,您无需拨打this.editor.focus()
。例如,这可以在official rich text example:
class StyleButton extends React.Component {
constructor() {
super();
this.onToggle = (e) => {
e.preventDefault();
this.props.onToggle(this.props.style);
};
}
render() {
let className = 'RichEditor-styleButton';
if (this.props.active) {
className += ' RichEditor-activeButton';
}
return (
<span className={className} onMouseDown={this.onToggle}>
{this.props.label}
</span>
);
}
}