Ребенка реквизит запускает рендеринг при использовании карты и редукса - PullRequest
0 голосов
/ 13 ноября 2018

Я использую Redux для создания приложения викторины, которое включает в себя форму с некоторыми вложенными полями. Я только что понял (я думаю), что каждое нажатие клавиши в моих полях ввода вызывает повторный рендеринг, если я использую опору детей, то есть проектирую приложение следующим образом:

const keys = Object.keys(state)

<QuizContainer>
  {keys.map(key => 
    <QuizForm key={key}>
        {state[key].questions.map(({ questionId }) => 
           <Question key={questionId} questionId={questionId}>
               {state[key]questions[questionId].answers.map(({ answerId })=>
                   <Answer answerId={answerId} key={answerId} />
               )}
           </Question>
        )}
    </QuizForm>
  )}
</QuizContainer>

QuizContainer подключен к редуксу с помощью mapStateToProps и mapDispatchToProps и выдает массив массивов, в которых все объекты содержат внутри себя. Структура магазина разработана в соответствии с «Руководством по вложениям редуксов» Дана Абрамова (с использованием типа магазина, подобного реляционной базе данных) и может быть описана следующим образом.

{
    ['quizId']: {
        questions: ['questionId1'],
        ['questionId1']:
            { question: 'some question', 
                answers: ['answerId1', 'answerId2']
            },
        ['answerId1']: { some: 'answer'},
        ['answerId2']: { some: 'other answer'}
 }

Приведенный выше код работает с точки зрения всего, что обновляется и т. Д. И т. Д., Без ошибок, но он вызывает безумное количество повторных рендеров, но ТОЛЬКО если я использую синтаксис композиции. Если я помещаю каждый компонент в другой (т.е. не использую props.children) и просто отправляю ключ quizId (и другие id-номера в качестве реквизитов), он работает как положено - без сумасшедшего повторного рендеринга. Чтобы быть кристально чистым, это работает, когда я делаю что-то вроде этого:

// quizId and questionId being passed from parent component's map-function (QuizForm)

const Question ({ answers }) => 
    <>
      {answers.map(({ answerId }) => 
        <Answer key={answerId} answerId={answerId} />
      )}
    </>

const mapStateToProps = (state, { questionId, quizId }) => ({
    answers: state[quizId][questionId].answers
})

export default connect(mapStateToProps)(Question)

Но ПОЧЕМУ? Какая разница между двумя? Я понимаю, что один из них передается родителю как опора, а не как ребенок того самого родителя, так сказать, но почему это в итоге дает другой результат? Разве это не значит, что они должны быть равны, но учитывают лучший синтаксис?

Редактировать: Теперь я могу убедиться, что детская опора вызывает проблему. Настройка

shouldComponentUpdate(nextProps) {
    if (nextProps.children.length === this.props.children.length) {
       return false
       } else {
       return true
    }
}

исправляет проблему. Тем не менее, похоже, что это довольно «черный ящик», не совсем уверенный в том, что я упускаю прямо сейчас ...

Ответы [ 2 ]

0 голосов
/ 13 ноября 2018

Хорошо, я понял:

Я сделал две плохие вещи:

  1. Мой верхний компонент был подключен к состоянию следующим образом: mapStateToProps (state) => ({keys: Object.keys (state)}). Я думал, что объектная функция вернет «статический» массив и не даст мне слушать все состояние, но оказалось, что я ошибся. Очевидно (для меня сейчас), каждый раз, когда я менял состояние, я получал новый массив (но с теми же записями). Теперь я храню их один раз в совершенно отдельном свойстве, называемом quizIds.

  2. Я поставил свою карту в плохое место. Теперь я продолжаю рендерить QuizContainer следующим образом:

     <QuizContainer>
       {quizIds.map(quizId => 
         <QuizForm>
          <Question>
           <Answer />
          </Question>
         </QuizForm>
       )}
     </QuizContainer>
    

А потом я рендерил свои массивы детей, вводя реквизиты, чтобы они могли использовать соединение по отдельности, вот так:

    {questions.map((questionId, index) => (
      <React.Fragment key={questionId}>
        {React.cloneElement(this.props.children, {
          index,
          questionId,
          quizId
        })}
      </React.Fragment>
    ))}

Этот последний фрагмент кода не будет работать, если вы решите поместить несколько элементов в качестве дочерних. Во всяком случае, выглядит чище и работает лучше сейчас! : D

0 голосов
/ 13 ноября 2018

Когда вы используете шаблон рендеринга, вы фактически объявляете новую функцию каждый раз, когда компонент выполняет рендеринг. Поэтому любое поверхностное сравнение между props.children не удастся. Это известный недостаток к шаблону, и ваше решение «черного ящика» является действительным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...