React DnD: избегать использования findDOMNode
Я не совсем понимаю это, но, по-видимому, не рекомендуется использовать findDOMNode().
Я пытаюсь создать компонент перетаскивания, но я не уверен, как мне обращаться к refs из переменной компонента. Это пример того, что у меня есть:
const cardTarget = {
hover(props, monitor, component) {
...
// Determine rectangle on screen
const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
...
}
}
Источник
Edit
Это может быть вызвано тем, что мой компонент является как источником перетаскивания, так и целью, поскольку я могу заставить его работать в в этом примере, но не этот.
Ответы
Ответ 1
Предполагая, что вы используете синтаксис класса es6 и самую последнюю версию React (15, на момент написания), вы можете прикрепить обратный вызов, как это сделал Дэн в своем примере по ссылке, которую вы поделили. Из документы:
Когда атрибут ref используется в элементе HTML, обратный вызов ref получает в качестве аргумента базовый элемент DOM. Например, этот код использует обратный вызов ref для хранения ссылки на DOM node:
<h3
className="widget"
onMouseOver={ this.handleHover.bind( this ) }
ref={node => this.node = node}
>
Затем вы можете получить доступ к node так же, как мы привыкли делать с нашими старыми друзьями findDOMNode()
или getDOMNode()
:
handleHover() {
const rect = this.node.getBoundingClientRect(); // Your DOM node
this.setState({ rect });
}
В действии:
https://jsfiddle.net/ftub8ro6/
Edit:
Поскольку React DND делает немного магии за кулисами, мы должны использовать их API, чтобы получить украшенный компонент. Они обеспечивают getDecoratedComponentInstance()
, чтобы вы могли получить базовый компонент. Как только вы это сделаете, вы можете получить component.node
, как ожидалось:
hover(props, monitor, component) {
const dragIndex = monitor.getItem().index;
const hoverIndex = props.index;
const rawComponent = component.getDecoratedComponentInstance();
console.log( rawComponent.node.getBoundingClientRect() );
...
Здесь он находится в действии:
https://jsfiddle.net/h4w4btz9/2/
Ответ 2
Лучшее решение
Лучшим решением является просто переносить свой перетаскиваемый компонент с помощью div
, определить ссылку на него и передать его перетаскиваемому компоненту, т.е.
<div key={key} ref={node => { this.node = node; }}>
<MyComponent
node={this.node}
/>
</div>
и MyComponent
завернуты в DragSource
. Теперь вы можете просто использовать
hover(props, monitor, component) {
...
props.node && props.node.getBoundingClientRect();
...
}
(props.node &&
просто добавлен, чтобы избежать вызова getBoundingClientRect
объекта undefined)
Альтернатива для findDOMNode
Если вы не хотите добавлять обертку div
, вы можете сделать следующее.
Ответ @imjared и предлагаемого решения здесь не работает (по крайней мере, в [email protected]
и [email protected]
).
Единственной рабочей альтернативой для findDOMNode(component).getBoundingClientRect();
, которая не использует findDOMNode
, является:
hover(props, monitor, component) {
...
component.decoratedComponentInstance._reactInternalInstance._renderedComponent._hostNode.getBoundingClientRect();
...
}
который не очень красив и опасен, потому что react
может изменить этот внутренний путь в будущих версиях!
Другое (более слабое) Альтернатива
Используйте monitor.getDifferenceFromInitialOffset();
, который не даст вам точных значений, но, возможно, достаточно хорош, если у вас есть небольшой dragSource. Тогда возвращаемое значение довольно предсказуемо с небольшим размером ошибки в зависимости от размера вашего dragSource.
Ответ 3
React-DnD
API очень гибкий - мы можем (ab) использовать это.
Например, React-DnD позволяет нам определить, какие соединители передаются базовому компоненту. Это означает, что мы тоже можем их обернуть.:)
Например, допустим переопределение целевого соединителя для хранения node на мониторе. Мы будем использовать Symbol
, чтобы мы не пропустили этот маленький взлом во внешний мир.
const NODE = Symbol('Node')
function targetCollector(connect, monitor) {
const connectDropTarget = connect.dropTarget()
return {
// Consumer does not have to know what we're doing ;)
connectDropTarget: node => {
monitor[NODE] = node
connectDropTarget(node)
}
}
}
Теперь в вашем методе hover
вы можете использовать
const node = monitor[NODE]
const hoverBoundingRect = node.getBoundingClientRect()
Этот подход объединяет поток React-DnD и экранирует внешний мир с помощью Symbol
.
Используете ли вы этот подход или подход на основе класса this.node = node
ref, вы полагаетесь на базовый React node. Я предпочитаю это, потому что потребитель не должен забывать вручную использовать ref
, кроме тех, которые уже требуются React-DnD, и потребитель тоже не должен быть компонентом класса.