Как избежать повторного рендеринга в React?

#javascript #reactjs #editor #accordion

#javascript #reactjs #редактор #аккордеон

Вопрос:

Я создаю простой аккордеон, внутри которого есть текстовый редактор.

Если мы нажмем развернуть текст, откроется текстовый редактор, а если мы введем некоторый текст внутри редактора и нажмем сжать, то аккордеон закроется.

Опять же, если щелкнуть по развернутому тексту accordion, где мы внесли изменения, то уже введенный текст в нем отсутствует.

Я могу понять, что это повторный рендеринг каждый раз, когда мы нажимаем на развернутый текст. Также этот код,

 <Text>  {toggleValue === index amp;amp; item.content amp;amp; <EditorContainer />} </Text>
  

проверьте, нажат ли элемент, затем он открывается, поэтому здесь происходит повторный рендеринг, и, следовательно, я теряю введенный текст.

Полный рабочий пример:

https://codesandbox.io/s/react-accordion-forked-dcqbo

Не могли бы вы любезно помочь мне сохранить значение, введенное в текстовом редакторе, несмотря на щелчки по тексту Expand / Shrink?

Ответ №1:

Поместите состояние редактора в постоянный родительский компонент. Поскольку NormalAccordion включает в себя все редакторы, и вам нужно постоянное состояние только одного редактора, используйте другой компонент, чтобы состояние не терялось при отключении редактора, затем передайте его редактору для использования:

 const OuterEditorContainer = ({ toggleValue, setToggleValue, item, index }) => {
  const [editorState, setEditorState] = useState(EditorState.createEmpty());

  const toggleHandler = (index) => {
    index === toggleValue ? setToggleValue(-1) : setToggleValue(index);
  };
  return (
    <Accordion>
      <Heading>
        <div
          style={{ padding: "10px", cursor: "pointer" }}
          className="heading"
          onClick={() => toggleHandler(index)}
        >
          {toggleValue !== index ? `Expand` : `Shrink`}
        </div>
      </Heading>
      <Text>
        {toggleValue === index amp;amp; item.content amp;amp; (
          <EditorContainer {...{ editorState, setEditorState }} />
        )}
      </Text>
    </Accordion>
  );
};
const NormalAccordion = () => {
  const [toggleValue, setToggleValue] = useState(-1);
  return (
    <div className="wrapper">
      {accordionData.map((item, index) => (
        <OuterEditorContainer
          {...{ toggleValue, setToggleValue, item, index }}
        />
      ))}
    </div>
  );
};
  
 // text_editor.js
export default ({ editorState, setEditorState }) => (
  <div className="editor">
    <Editor
      editorState={editorState}
      onEditorStateChange={setEditorState}
      toolbar={{
        inline: { inDropdown: true },
        list: { inDropdown: true },
        textAlign: { inDropdown: true },
        link: { inDropdown: true },
        history: { inDropdown: true }
      }}
    />
  </div>
);
  

Вы также можете поместить состояние в text_editor само по себе и всегда отображать этот контейнер, но только условно отображать <Editor .

Комментарии:

1. Спасибо за ваш ответ.. Не могли бы вы, пожалуйста, предоставить рабочую изолированную среду, разветвив мою в вопросе?

2. Изменения в ответе — это действительно все, что вам нужно, но если вам тоже нужна ссылка: codesandbox.io/s/react-accordion-forked-mi7s0?file=/src /…

3. Еще раз спасибо за предоставление ссылки.. Но здесь, если я ввожу текст внутри любого редактора, и если я закрываю этот аккордеон, и если я открываю любой другой аккордеон, то ранее введенный текст присутствует внутри другого аккордеона, который не имеет значения.. Не могли бы вы, пожалуйста, проверить это?

4. Введенный текст внутри редактора связан только с этим конкретным аккордеоном.. Теперь изменение в любом текстовом редакторе отражается во всех других текстовых редакторах..

5. О, у вас есть .map , так что у них у всех было бы одинаковое состояние. Используйте разные контейнеры для каждого редактора и вместо этого помещайте в них постоянное состояние для каждого.

Ответ №2:

Вам нужно сохранить введенный текст и передать его в качестве реквизита из родительского компонента в EditorContainer.

Прямо сейчас каждый раз, когда вы его визуализируете (например, когда мы нажимаем развернуть) Похоже, вы установили пустое состояние.

Что-то вроде:

EditorContainer

   editorState: this.props.editorState || EditorState.createEmpty()

  onEditorStateChange = (editorState) => {
    // console.log(editorState)
    this.props.setEditorState(editorState);
  };
  

И в аккордеоне:

 {toggleValue === index amp;amp; 
 item.content amp;amp; 
<EditorContainer 
editorState={this.state.editorState[index]} 
setEditorState={newText => this.setState({...this.state, newText}) />}
  

Не пытался выполнить это, но я думаю, что это способ добиться этого.
PS: Компоненты класса почти больше не используются. Попробуйте использовать функциональные компоненты и узнайте о крючке useState, на мой взгляд, выглядит намного чище