运用innerRef

如果以前你还没有用过ref,请看看官方文档React: Refs 和 DOM 指南

我们的DraggableDroppable组件,都要求拥有一个HTMLElement。通过使用DraggableProvidedinnerRef属性和DroppableProvided对象.

  1. <Draggable draggableId="draggable-1" index={0}>
  2. {(provided, snapshot) => (
  3. <div
  4. + ref={provided.innerRef}
  5. {...provided.draggableProps}
  6. {...provided.dragHandleProps}
  7. >
  8. <h4>My draggable</h4>
  9. </div>
  10. )}
  11. </Draggable>;
  1. <Droppable droppableId="droppable-1">
  2. {(provided, snapshot) => (
  3. <div
  4. + ref={provided.innerRef}
  5. {...provided.droppableProps}
  6. >
  7. <h2>I am a droppable!</h2>
  8. {provided.placeholder}
  9. </div>
  10. )}
  11. </Droppable>;

不是全部的ref,都生而一样

混乱可能来自,Reactref回调工作的方式.

在一个Component,如<Person />ref回调,将返回PersonComponent的实例-instance.

在一个ReactElement,如<div />ref回调,将返回与ReactElement有关的HTML 元素.

codesandbox.io

  1. class Person extends React.Component {
  2. state = {
  3. sayHello: false
  4. };
  5. sayHello() {
  6. this.setState({
  7. sayHello: true
  8. });
  9. }
  10. render() {
  11. if (this.state.sayHello) {
  12. return <div {...this.props}>Hello</div>;
  13. }
  14. return <div {...this.props}>'I am a person, I think..'</div>;
  15. }
  16. }
  17. class App extends React.Component {
  18. setPersonRef = ref => {
  19. this.personRef = ref;
  20. // 当 ref一改变, 它被设为 null
  21. if (this.personRef) {
  22. // personRef 是 Person 类的一个实例
  23. this.personRef.sayHello();
  24. }
  25. };
  26. setDivRef = ref => {
  27. this.divRef = ref;
  28. if (this.divRef) {
  29. // div ref 是 HTMLElement
  30. this.divRef.style.backgroundColor = 'lightgreen';
  31. }
  32. };
  33. render() {
  34. return (
  35. <React.Fragment>
  36. <Person ref={this.setPersonRef} />
  37. <div ref={this.setDivRef}>hi there</div>
  38. </React.Fragment>
  39. );
  40. }
  41. }

一个常见的错误 🐞

看看这个例子:

  1. <Draggable draggableId="draggable-1" index={0}>
  2. {(provided, snapshot) => (
  3. <Person
  4. ref={provided.innerRef}
  5. {...provided.draggableProps}
  6. {...provided.dragHandleProps}
  7. />
  8. )}
  9. </Draggable>

虽然它看起来是正确的,但它会导致你的应用程序爆炸 💥!

这是因为react-beautiful-dnd期待提供给Draggableprovided.innerRef函数,和Droppable的调用要使用组件的 DOM 节点,而不是类的实例。在这个例子中我们正在调用provided.innerRefPerson实例,而不是底层的 DOM 节点.

从组件中公开 DOM ref

一种简单的公开你组件的HTML 元素,就是创造你自己的innerRef porps:

  1. class Person extends React.Component {
  2. render() {
  3. return (
  4. <div {...this.props} ref={this.props.innerRef}>
  5. I am a person, I think..
  6. </div>
  7. );
  8. }
  9. }

注意,名称innerRef只是一个惯例。您可以为组件调用它。就像是domRef一样样的.

然后,您可以正确地将 DOM 节点提供给 一个Draggable要么Droppable

  1. <Draggable draggableId="draggable-1" index={0}>
  2. {(provided, snapshot) => (
  3. <Person
  4. - ref={provided.innerRef}
  5. + innerRef={provided.innerRef}
  6. {...provided.draggableProps}
  7. {...provided.dragHandleProps}
  8. >
  9. <h4>My draggable</h4>
  10. </div>
  11. )}
  12. </Draggable>

⚠️ 这种方法会导致 一个React警告,因为我们正在将组件的{...this.props}所有props-道具传播到 DOM 节点上。这包括innerRef,而React不喜欢你添加到元素。所以你可以像这样设置:

  1. class Person extends React.Component {
  2. render() {
  3. - return (
  4. - <div {...this.props} ref={this.props.innerRef}>
  5. - I am a person, I think..
  6. - </div>
  7. - );
  8. }
  9. }
  10. class Person extends React.Component {
  11. render() {
  12. + const { provided, innerRef } = this.props;
  13. + return (
  14. + <div
  15. + {...provided.draggableProps}
  16. + {...provided.dragHandleProps}
  17. + ref={innerRef}
  18. + >
  19. + I am a person, I think..
  20. + </div>
  21. + );
  22. }
  23. }
  24. <Draggable draggableId="draggable-1" index={0}>
  25. {(provided, snapshot) => (
  26. <Person
  27. innerRef={provided.innerRef}
  28. - {...provided.draggableProps}
  29. - {...provided.dragHandleProps}
  30. + provided={provided}
  31. />
  32. )}
  33. </Draggable>

如果你还需要在你的Component使用HTML 元素,那你需要有一个更强大的 ref 设置方法:

  1. class Person extends React.Component {
  2. setRef = ref => {
  3. // 保持 reference 到 dom ref 作为 实例的属性
  4. this.ref = ref;
  5. // 将 dom ref 给予 react-beautiful-dnd
  6. this.props.innerRef(ref);
  7. };
  8. render() {
  9. const {provided, innerRef} = this.props;
  10. return (
  11. <div
  12. {...provided.draggableProps}
  13. {...provided.dragHandleProps}
  14. ref={this.setRef}
  15. >
  16. I am a person, I think..
  17. </div>
  18. );
  19. }
  20. }

把它们放在一起

以下示例展示了,本指南中提供的知识:https://codesandbox.io/s/v3p0q71qn5

关于 SVG 的说明

react-beautiful-dnd不支持拖动<svg>元素.包裹你的<svg>到一个HTMLElement,如<span>要么<div>,此方式提供良好的无障碍和跨浏览器支持。见我们的使用 SVGs 指南获得更多信息.