Сохранение ссылки на дочернюю функцию в родительской

У меня есть интересный вариант использования для хранения ссылок на функцию, принадлежащую дочернему компоненту. Кстати, я использую новый контекстный API React для передачи данных глубоко вложенным дочерним компонентам.

Я нашел ряд других ответов, посвященных аналогичным проблемам, но они не совсем соответствуют моему варианту использования. См. здесь и здесь.

Это проблема высокого уровня:

  1. Компонент Provider под названием <Parent /> содержит логику и состояние, которые передаются всем компонентам <Child />, которые являются Consumers.
  2. Компоненты Child получают опору verify, которая, по сути, является своего рода функцией валидатора для этого конкретного Child. Каждый вызов verify создает verificationResult на основе изменений входящего состояния от Parent.
  3. Важно отметить, что все остальные компоненты Child должны быть уведомлены или знать результат каждого verificationResult, произведенного его братьями и сестрами.
  4. Чтобы сделать вещи более интересными, я бы предпочел не хранить так называемый verificationResult каждого дочернего элемента в родительском состоянии, если мне это не нужно, поскольку это по сути производное состояние и может быть вычислено в Parentrender().

Решение 1:

хранить verificationResult каждого ребенка в Parent. Это можно сделать, дождавшись изменения соответствующего значения в Child каждого componentDidUpdate(), например:

// Child.js
...
componentDidUpdate(pp) {
  const { hash, value, verify, setVerificationResult } = this.props

  // this check is a little more involved but 
  // the next line captures the gist of it. 
  if (pp.value[pp.hash] !== value[hash]) {
    setVerificationResult(hash, verify(value[hash], value))
  }
}
...

// Parent.js
...
setVerificationResult(hash, result) {
 this.setState(({ verificationResults }) => ({
    ...verificationResults, [hash]: result
 })) 
}
...

Примечание:

  • this.props.value и this.props.setVerificationResult принимаются от Parent, который является контекстом Provider, в то время как

  • this.props.hash и this.props.verify передаются компоненту Child напрямую.

this.props.hash захватывает часть value, о которой этот конкретный Child должен знать.

// MyComponent.js

render() {
  return (
    ...
    <Child 
      hash = "someHash"
      verify = {(value, allValues) => {
        return value === 42 && allValues["anotherHash"] === 42
      }}
      render = {({ verificationResults }) => (
        <pre>{JSON.stringify(verificationResults["someHash"], null, ' ')}</pre>
      )}
    /> 
   ...
)

Решение 1 соответствует принципу однонаправленного потока данных, который поощряет реагирование. Однако у меня есть 2 проблемы с этим решением. Во-первых, мне нужно сохранить то, что по сути является состоянием, которое можно вывести. Во-вторых, вызов setVerificationResult() приведет к нежелательному повторному рендерингу. Не говоря уже о дополнительной логике в componentDidUpdate

Решение 2

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

// Child.js
...
componentDidMount() {
  const { register } = this.props
  register(this.verify)
}

verify(values) {
  const { hash, verify } = this.props
  return { [hash]: verify(values[hash], values) }
}
...

// Parent.js
constructor(props)
  super(props)
  this.tests = [] 
  this.state = { 
    value: null, 
    // other initial state
  }
  ...
}

register(verifyFunc) {
  this.tests.push(verifyFunc)
}

render() {
  const { value } = this.sate
  let result = {}
  this.tests.forEach(test => result = { ...result, ...test(value) })
  return (
    <Provider 
      value = {{
        ...this.state,
        verificationResults: result,
        register: this.register,
        // other things...
     }}
    >
      {this.props.children}
    </Provider>
  )
}

Обратите внимание, что во втором решении я не сохраняю никакого дополнительного состояния, так как оно просто вычисляется на лету. Однако я храню ссылки на функцию в дочернем компоненте.

Может кто подскажет, зачем НЕТ использовать решение 2? Любые альтернативные предложения о том, как заставить эту работу работать?

Дополнительные замечания:

  • Почему бы не передать функцию проверки родительскому элементу напрямую? Я мог бы, но это не сделало бы очень чистый API.
  • Вы можете предположить, что я выполняю необходимую очистку при размонтировании и т. д.
  • Проблема более сложна, чем показано здесь - на самом деле существуют глубоко вложенные потребители, которые общаются с «локальным» провайдером, который «разделяет» значения и логику, пока они не достигнут так называемых листовых компонентов, в то время как «главный» провайдер используется в качестве корневого. узел, содержащий все состояние компонентов и логику в иерархии компонентов.

Не могли бы вы пояснить, что вы имеете в виду под «все другие дочерние компоненты должны быть уведомлены или знать результат каждого verifyResult, созданного его братьями и сестрами». В каком-то смысле, почему их нужно уведомлять? И есть ли способ их уведомить с помощью какой-либо службы? (своего рода архитектура типа потока)

Yftach 30.05.2018 13:41

По вашему второму вопросу о том, почему бы не использовать решение номер 2: честно говоря, вы можете, однако это не рекомендуется и не реагирует на это. Под этим я подразумеваю, что эта реакция была создана с определенным мышлением, когда все изменения в приложении управляются ДАННЫМИ. Если все управляется данными, родительский компонент не должен иметь возможность независимо заставлять дочерний компонент что-то делать, он должен изменять данные и сообщать дочернему элементу, чтобы увидеть, применимы ли к ним какие-либо из этих изменений данных.

Yftach 30.05.2018 13:46

Это сделано для того, чтобы все было организовано, вся логика дочернего компонента и логика того, когда что-то происходит, находятся в дочернем компоненте.

Yftach 30.05.2018 13:46

1. Я хочу, чтобы это была чистая реакция. Как можно меньше зависимостей - это вдвойне для библиотеки управления состоянием. 2. Возможно, уведомление - неправильное слово. По сути, проблема заключается в том, что опора для дочернего элемента должна вносить изменения в состояние в родительском элементе, и этот бит состояния должен быть доступен для всех потребителей. Однако, когда вы думаете о проверке (в данном случае я просто называю ее проверкой), проверка - это продукт текущего состояния некоторого значения или значений. Его не нужно хранить в состоянии (это одна из причин, по которой я предпочитаю решение 2).

Stuart Bourhill 30.05.2018 14:05

Вы можете реализовать поток самостоятельно, однако это может оказаться излишним, если вы просто воспользуетесь им из-за этого. В конечном счете, это вопрос суждения, которое можете сделать только вы, потому что у вас есть вся информация о типе проекта, который вы строите.

Yftach 30.05.2018 14:14
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
5
87
0

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