Я пытаюсь передать состояние редактора Draft.js из компонента редактора в свой собственный компонент Sidebar
.
Используя самый верхний компонент Notes
, я использую обратный вызов, чтобы получить состояние редактора от CustomEditor
и установить его как состояние Notes
. Затем я передаю это состояние в Sidebar
как опору.
Проблема в том, что опора устанавливается до срабатывания обратного вызова. Я думал о setTimeout
, но это кажется грубым. Я знаю о UNSAFE_componentWillReceiveProps()
, но в документации его не рекомендуют. Есть ли что-то в этом случае?
export class Notes extends Component {
constructor(props) {
super(props);
this.getEditorState = this.getEditorState.bind(this)
this.state = {
editorState: "the placeholder data Sidebar should not have as a prop"
};
}
getEditorState(state) {
console.info(state)
this.setState({editorState: state})
}
render() {
return (
<section id = "Notes">
<div id = "editor-holder">
<Sidebar currentEditorState = {this.state.editorState}/>
<div id = "Editor">
<FileHeader />
<CustomEditor getState = {this.getEditorState}/>
</div>
</div>
</section>
);
}
}
export default Notes;
Что ж, есть еще варианты, как добиться такого результата
Вы можете визуализировать <Sidebar>
только тогда, когда реквизит изменил это меню.
constructor(props)
super(props)
this.state = {
editorState: false
}
}
render() {
... {this.state.editorState && <Sidebar currentEditorState = {this.state.editorState}/>}
}
Sidebar.js
render() {
if (!this.props.currentEditorState) return null // this will force React to render nothing
return ( ... )
}
https://reactjs.org/docs/react-component.html#static-getdehibitedstatefromprops
Sidebar.js
static getDerivedStateFromProps({ currentEditorState }, prevState) {
if (currentEditorState !== false {
return { currentEditorState }
} else {
return {}
}
}
render() {
(.... something bound to this.state.currentEditorState)
}
class Notes extends React.Component {
getEditorState(state) {
console.info(state)
this.setState({editorState: state})
}
getChildContext() {
return {
editorState: this.state.editorState
}
}
childContextTypes = {
editorState: PropTypes.oneOfType([PropTypes.obj, PropTypes.bool])
}
}
Sidebar.js
class Sidebar {
static contextTypes = {
editorState: PropTypes.oneOfType([PropTypes.obj, PropTypes.bool])
}
render() {
... this.context.editorState
}
}
Ваш случай совершенно нормальный, часто рендеринг дочерних элементов с реквизитами postponed
, например, когда родительский компонент действует как загрузчик данных и передает реквизиты вниз. Такой же поток выполняется, например. в модальных окнах, где у вас есть кнопка, которая устанавливает для state.isOpen
значение true, и открывается дочерний модальный компонент. Однако передача состояния от одного компонента к другому выглядит как антишаблон. Я бы подумал о централизованном хранилище как redux / mobx, или, если вы не хотите его использовать, вы можете использовать React context
, который автоматически разделяет состояние.
Я рассмотрю context
как решение этой проблемы. Похоже, это решение React в этом случае.
Судя по тому, что я читал о документах, вы используете старый контекстный API. Это потому, что устаревший контекстный API лучше всего подходит для этого случая? Я понимаю, что новый API требует много работы для решения этой небольшой проблемы. См. responsejs.org/docs/legacy-context.html#updating-context
Это наследие «будущего». Новый был представлен 30 дней назад, и я не тестировал его в производственной среде, поэтому я не хочу публиковать код, с которым я не знаком, но не стесняйтесь использовать новый контекст, конечно
Я займусь всем новым контекстным API, поскольку мне кажется, что это то, что мне нужно, если я смогу заставить его работать. А пока спасибо за инициативу.
Новый Context API - это решение этого типа проблем. Потребовалось немного времени, чтобы осмыслить это, но то, что я придумал, использует editorState
в качестве опоры для Sidebar
.
export const NoteContext = React.createContext("placeholderEditorState");
export class Notes extends Component {
constructor(props) {
super(props);
this.getEditorState = this.getEditorState.bind(this)
this.getFolderData = this.getFolderData.bind(this)
this.state = {
editorState: null,
folderData: null
};
}
getEditorState(state) {
this.setState({editorState: state});
}
getFolderData(data) {
this.setState({folderData : data})
}
render() {
return (
<section id = "Notes">
<TopBar />
<div id = "editor-holder">
<NoteContext.Provider value = {{editorState: this.state.editorState}} >
<NoteContext.Consumer>
{(context)=>{ return (
<Sidebar currentEditorState = {context.editorState} getFolderData = {this.getFolderData}/>
)}}
</NoteContext.Consumer>
</NoteContext.Provider>
<div id = "Editor">
<NoteContext.Provider value = {{folderData: this.state.folderData}} >
<FileHeader />
</NoteContext.Provider>
<CustomEditor getState = {this.getEditorState}/>
</div>
</div>
</section>
);
}
}
Глядя на это сейчас, это кажется очень простым, а это значит, что я многому научился! Сообщите мне, могу ли я здесь что-нибудь улучшить.
Правильно ли я полагаю, что мой вариант использования в чем-то уникален? Как вы думаете, есть ли лучший способ спроектировать мой поток данных?