Реагировать на обновление дочерних реквизитов после изменения родительского состояния

Я пытаюсь передать состояние редактора 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;
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
2
0
1 750
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Что ж, есть еще варианты, как добиться такого результата

Условный рендеринг

Вы можете визуализировать <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 ( ... )
}

Перевести свойства в состояние с помощью getDerivedState

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
  }
}

Правильно ли я полагаю, что мой вариант использования в чем-то уникален? Как вы думаете, есть ли лучший способ спроектировать мой поток данных?

Draxy 02.05.2018 04:15

Ваш случай совершенно нормальный, часто рендеринг дочерних элементов с реквизитами postponed, например, когда родительский компонент действует как загрузчик данных и передает реквизиты вниз. Такой же поток выполняется, например. в модальных окнах, где у вас есть кнопка, которая устанавливает для state.isOpen значение true, и открывается дочерний модальный компонент. Однако передача состояния от одного компонента к другому выглядит как антишаблон. Я бы подумал о централизованном хранилище как redux / mobx, или, если вы не хотите его использовать, вы можете использовать React context, который автоматически разделяет состояние.

Milos Mosovsky 02.05.2018 04:19

Я рассмотрю context как решение этой проблемы. Похоже, это решение React в этом случае.

Draxy 02.05.2018 04:24

Судя по тому, что я читал о документах, вы используете старый контекстный API. Это потому, что устаревший контекстный API лучше всего подходит для этого случая? Я понимаю, что новый API требует много работы для решения этой небольшой проблемы. См. responsejs.org/docs/legacy-context.html#updating-context

Draxy 02.05.2018 04:42

Это наследие «будущего». Новый был представлен 30 дней назад, и я не тестировал его в производственной среде, поэтому я не хочу публиковать код, с которым я не знаком, но не стесняйтесь использовать новый контекст, конечно

Milos Mosovsky 02.05.2018 04:44

Я займусь всем новым контекстным API, поскольку мне кажется, что это то, что мне нужно, если я смогу заставить его работать. А пока спасибо за инициативу.

Draxy 02.05.2018 04:47
Ответ принят как подходящий

Новый 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>
      ); 
  }
}

Глядя на это сейчас, это кажется очень простым, а это значит, что я многому научился! Сообщите мне, могу ли я здесь что-нибудь улучшить.

Другие вопросы по теме