「React基本功」这篇文章将解释refs中 哪些不建议使用,哪些推荐使用?

243 阅读4分钟

在 React 中,refs 是一个强大的工具,用于直接访问 DOM 元素或子组件的实例。然而,由于它打破了 React 声明式数据流的基本原则,在一些场景中不建议使用,而在某些场景中使用 refs 是合适且推荐的。

一、哪些场景不建议使用 refs

  1. 避免用 refs 替代状态管理 不推荐原因refs 不应被用来管理组件的状态或传递数据。React 的核心理念是通过 propsstate 进行数据流的管理。如果使用 refs 来保存数据,会让代码难以维护和调试,且会打破 React 的声明式编程方式。

    替代方案:使用 useStateprops 来管理和传递状态。在 React 中,状态应通过 setState(类组件)或 useState(函数组件)进行管理,而不是通过 refs

  2. 不建议使用 refs 进行组件间通信 不推荐原因:通过 refs 直接访问子组件的实例,进行父子组件通信,打破了 React 的单向数据流。组件之间的通信应当通过 propscallbacks 来完成,这样可以确保数据流和更新的可追踪性。

    替代方案:使用 props 进行父子组件间的数据传递,使用回调函数处理事件。如果需要复杂的组件通信,考虑使用 Context API 或状态管理工具(如 Redux)。

  3. 避免使用 refs 做过多的 DOM 操作 不推荐原因:React 通过虚拟 DOM 来高效管理实际 DOM 的操作。如果频繁使用 refs 来直接操作 DOM,会与 React 的虚拟 DOM 机制相冲突,并且可能造成不必要的性能问题。

    替代方案:如果可以使用 React 的声明式渲染和更新机制,就应尽量避免手动 DOM 操作。例如,在处理动画时,可以借助 CSS 或 React 的生命周期方法而不是频繁操作 DOM。

二、哪些场景推荐使用 refs

  1. 直接访问 DOM 元素(如表单、焦点控制等) 推荐原因:在某些情况下,确实需要直接访问 DOM 元素,例如:

    • 聚焦输入框:在组件加载后自动聚焦某个表单元素,或用户点击某个按钮后聚焦输入框。
    • 滚动事件:当需要手动滚动到某个特定位置,或控制滚动行为时,refs 非常有用。
    • 媒体播放控制:如手动播放/暂停视频或音频元素,refs 可以帮助直接操作这些 DOM 元素。

    示例:

    function MyInput() {
      const inputRef = useRef(null);
    
      useEffect(() => {
        inputRef.current.focus();  // 在组件加载后自动聚焦输入框
      }, []);
    
      return <input ref={inputRef} />;
    }
    
  2. 集成第三方库 推荐原因:当使用一些需要直接操作 DOM 元素的第三方库时(如 jQuery、D3、Chart.js 等),refs 是必不可少的。这些库往往要求你手动访问 DOM 来进行初始化和操作,refs 可以作为桥梁。

    示例:

    function ChartComponent() {
      const chartRef = useRef(null);
    
      useEffect(() => {
        const chart = new Chart(chartRef.current, { /* 初始化 Chart.js */ });
      }, []);
    
      return <canvas ref={chartRef} />;
    }
    
  3. 控制动画或过渡效果 推荐原因:在需要手动控制动画或过渡效果时(例如使用 requestAnimationFrame),可以通过 refs 获取 DOM 元素并手动触发动画。这类场景下,声明式的方式可能无法满足需求,refs 提供了更高的灵活性。

    示例:

    function AnimationComponent() {
      const boxRef = useRef(null);
    
      const startAnimation = () => {
        boxRef.current.style.transform = 'translateX(100px)'; // 控制动画
      };
    
      return (
        <div>
          <div ref={boxRef} style={{ width: 100, height: 100, background: 'blue' }} />
          <button onClick={startAnimation}>Start Animation</button>
        </div>
      );
    }
    
  4. 访问类组件实例 推荐原因:在需要调用类组件中的实例方法时,refs 是有效的工具。虽然函数组件推荐使用 hooks,但在某些需要访问类组件方法的场景下,refs 依然是首选。

    示例:

    class ChildComponent extends React.Component {
      alertMessage() {
        alert('Hello from ChildComponent');
      }
    
      render() {
        return <div>Child Component</div>;
      }
    }
    
    class ParentComponent extends React.Component {
      constructor(props) {
        super(props);
        this.childRef = React.createRef();
      }
    
      handleClick = () => {
        this.childRef.current.alertMessage(); // 通过 ref 调用子组件方法
      }
    
      render() {
        return (
          <div>
            <ChildComponent ref={this.childRef} />
            <button onClick={this.handleClick}>Call Child Method</button>
          </div>
        );
      }
    }
    

三、refs 使用时的最佳实践

  1. 优先使用 useRef 在函数组件中,优先使用 useRef Hook 来创建 ref。它不仅能持久保存对 DOM 的引用,还能避免重新渲染的副作用。

  2. 尽量减少 refs 的使用 refs 主要用于操作 DOM 或子组件实例,而不应该滥用。在大多数情况下,React 的声明式编程能够更好地管理组件状态和行为,refs 应仅在必要时使用。

  3. 保持清晰的 refs 逻辑 使用 refs 时,务必确保其使用的场景合理且有必要。应尽量减少复杂 refs 逻辑,避免维护困难。

总结

  • 不建议使用 refs 的场景:包括代替状态管理、组件间通信以及频繁 DOM 操作。这些操作应通过 React 的声明式数据流和状态管理来完成。
  • 推荐使用 refs 的场景:包括直接操作 DOM 元素、集成第三方库、动画控制以及访问类组件实例。在这些场景下,refs 能提供必要的灵活性。

合理使用 refs,保持代码的简洁和维护性,是提升 React 开发效率的重要手段。