У меня есть интересный вариант использования для хранения ссылок на функцию, принадлежащую дочернему компоненту. Кстати, я использую новый контекстный API React для передачи данных глубоко вложенным дочерним компонентам.
Я нашел ряд других ответов, посвященных аналогичным проблемам, но они не совсем соответствуют моему варианту использования. См. здесь и здесь.
Это проблема высокого уровня:
Provider под названием <Parent /> содержит логику и состояние, которые передаются всем компонентам <Child />, которые являются Consumers.Child получают опору verify, которая, по сути, является своего рода функцией валидатора для этого конкретного Child. Каждый вызов verify создает verificationResult на основе изменений входящего состояния от Parent.Child должны быть уведомлены или знать результат каждого verificationResult, произведенного его братьями и сестрами.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? Любые альтернативные предложения о том, как заставить эту работу работать?
Дополнительные замечания:
По вашему второму вопросу о том, почему бы не использовать решение номер 2: честно говоря, вы можете, однако это не рекомендуется и не реагирует на это. Под этим я подразумеваю, что эта реакция была создана с определенным мышлением, когда все изменения в приложении управляются ДАННЫМИ. Если все управляется данными, родительский компонент не должен иметь возможность независимо заставлять дочерний компонент что-то делать, он должен изменять данные и сообщать дочернему элементу, чтобы увидеть, применимы ли к ним какие-либо из этих изменений данных.
Это сделано для того, чтобы все было организовано, вся логика дочернего компонента и логика того, когда что-то происходит, находятся в дочернем компоненте.
1. Я хочу, чтобы это была чистая реакция. Как можно меньше зависимостей - это вдвойне для библиотеки управления состоянием. 2. Возможно, уведомление - неправильное слово. По сути, проблема заключается в том, что опора для дочернего элемента должна вносить изменения в состояние в родительском элементе, и этот бит состояния должен быть доступен для всех потребителей. Однако, когда вы думаете о проверке (в данном случае я просто называю ее проверкой), проверка - это продукт текущего состояния некоторого значения или значений. Его не нужно хранить в состоянии (это одна из причин, по которой я предпочитаю решение 2).
Вы можете реализовать поток самостоятельно, однако это может оказаться излишним, если вы просто воспользуетесь им из-за этого. В конечном счете, это вопрос суждения, которое можете сделать только вы, потому что у вас есть вся информация о типе проекта, который вы строите.





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