网站首页 > 开源技术 正文
Refs and the DOM
何时使用 Refs
下面是几个适合使用 refs 的情况:
- 管理焦点,文本选择或媒体播放。
- 触发强制动画。
- 集成第三方 DOM 库。
避免使用 refs 来做任何可以通过声明式实现来完成的事情。
举个例子,避免在 Dialog 组件里暴露 open() 和 close() 方法,最好传递 isOpen 属性。
勿过度使用 Refs
你可能首先会想到使用refs 在你的 app 中“让事情发生”。如果是这种情况,请花一点时间,认真再考虑一下 state 属性应该被安排在哪个组件层中。通常你会想明白,让更高的组件层级拥有这个 state,是更恰当的。---状态提升
你不能在函数组件上使用 ref 属性,因为他们没有实例。函数式组件里的dom元素是可以添加ref的,组件不能
在高阶组件中转发 refs
refs 将不会透传下去。这是因为 ref不是 prop 属性。就像 key一样,其被 React 进行了特殊处理。如果你对 HOC 添加 ref,该 ref 将引用最外层的容器组件,而不是被包裹的组件。
幸运的是,我们可以使用React.forwardRefAPI 明确地将 refs 转发到内部的组件或者元素
function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }
    render() {
      const {forwardedRef, ...rest} = this.props;
      // 将自定义的 prop 属性 “forwardedRef” 定义为 ref
      return <Component ref={forwardedRef} {...rest} />;
    }
  }
  // 注意 React.forwardRef 回调的第二个参数 “ref”。
  // 我们可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
  // 然后它就可以被挂载到被 LogProps 包裹的子组件上。
  return React.forwardRef((props, ref) => {
    return <LogProps {...props} forwardedRef={ref} />;
  });
}
Refs 与函数组件
默认情况下,你不能在函数组件上使用 ref 属性,因为它们没有实例:
如果要在函数组件中使用 ref,你可以使用 forwardRef(可与 useImperativeHandle结合使用),或者可以将该组件转化为 class 组件。
不管怎样,你可以在函数组件内部使用 ref 属性,只要它指向一个 DOM 元素或 class 组件:
function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
回调 Refs
class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = null;
    this.setTextInputRef = element => {
      this.textInput = element;
    };
    this.focusTextInput = () => {
      // 使用原生 DOM API 使 text 输入框获得焦点
      if (this.textInput) this.textInput.focus();
    };
  }
  componentDidMount() {
    // 组件挂载后,让文本框自动获得焦点
    this.focusTextInput();
  }
  render() {
    // 使用 `ref` 的回调函数将 text 输入框 DOM 节点的引用存储到 React
    // 实例上(比如 this.textInput)
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}
React 将在组件挂载时,会调用 ref 回调函数并传入 DOM 元素,当卸载时调用它并传入 null。在 componentDidMount 或 componentDidUpdate 触发前,React 会保证 refs 一定是最新的。
如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。
你可以在组件间传递回调形式的 refs,就像你可以传递通过 React.createRef() 创建的对象 refs 一样。
function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}
class Parent extends React.Component {
  render() {
    return (
      <CustomTextInput
        inputRef={el => this.inputElement = el}
      />
    );
  }
}
Render Props
术语 “render prop”是指一种在React 组件之间使用一个值为render函数的 prop 共享代码的简单技术
具有 render prop 的组件接受一个函数,该函数返回一个 React 元素并调用它而不是实现自己的渲染逻辑。
<DataProvider render={data => (<h1>Hello {data.target}</h1>)}/>
使用 render prop 的库有 React Router、Downshift以及 Formik。
在这个文档中,我们将讨论为什么 render prop 是有用的,以及如何写一个自己的 render prop 组件。
使用 Render Props 来解决横切关注点(Cross-Cutting Concerns)
这也是 render prop 的来历:我们可以提供一个带有函数 prop 的 <Mouse> 组件,它能够动态决定什么需要渲染的,而不是将 <Cat> 硬编码到 <Mouse> 组件里,并有效地改变它的渲染结果。
import React, { PureComponent } from 'react'
class Cat extends React.Component {
    render() {
      const mouse = this.props.mouse;
      return (
        <img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
      );
    }
}
  
class Mouse extends React.Component {
    constructor(props) {
        super(props);
        this.handleMouseMove = this.handleMouseMove.bind(this);
        this.state = { x: 0, y: 0 };
    }
    handleMouseMove(event) {
        this.setState({
            x: event.clientX,
            y: event.clientY
        });
    }
render() {
    return (
    <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
        {/*
        Instead of providing a static representation of what <Mouse> renders,
        use the `render` prop to dynamically determine what to render.
        */}
        {this.props.render(this.state)}
    </div>
    );
}
}
class MouseTracker extends React.Component {
    render() {
        return (
            <div>
                <h1>移动鼠标!</h1>
                <Mouse render={mouse => (
                    <Cat mouse={mouse} />
                )}/>
            </div>
        );
    }
}
  
export default MouseTracker
重要的是要记住,render prop 是因为模式才被称为 renderprop ,你不一定要用名为 render的 prop 来使用这种模式。事实上, 任何被用于告知组件需要渲染什么内容的函数 prop 在技术上都可以被称为 “render prop”
注意事项
将 Render Props 与 React.PureComponent 一起使用时要小心
如果你在 render 方法里创建函数,那么使用 render prop 会抵消使用 React.PureComponent 带来的优势。因为浅比较 props 的时候总会得到 false,并且在这种情况下每一个 render 对于 render prop 将会生成一个新的值。
为了绕过这一问题,有时你可以定义一个 prop 作为实例方法,类似这样:
class MouseTracker extends React.Component {
  // 定义为实例方法,`this.renderTheCat`始终
  // 当我们在渲染中使用它时,它指的是相同的函数
  renderTheCat(mouse) {
    return <Cat mouse={mouse} />;
  }
  render() {
    return (
      <div>
        <h1>Move the mouse around!</h1>
        <Mouse render={this.renderTheCat} />
      </div>
    );
  }
}
如果你无法静态定义 prop(例如,因为你需要关闭组件的 props 和/或 state),则 <Mouse>应该扩展 React.Component。
React.memo
const MyComponent = React.memo(function MyComponent(props) {
  /* 使用 props 渲染 */
});
React.memo 为高阶组件。它与 React.PureComponent非常相似,但只适用于函数组件,而不适用 class 组件。
React.memo 仅检查 props 变更。如果函数组件被 React.memo 包裹,且其实现中有 useState或 useContext的 Hook,当 context 发生变化时,它仍会重新渲染。
默认情况下其只会对复杂对象做浅层对比,如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现。
function MyComponent(props) {
  /* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
  /*
  如果把 nextProps 传入 render 方法的返回结果与
  将 prevProps 传入 render 方法的返回结果一致则返回 true,
  否则返回 false
  */
}
export default React.memo(MyComponent, areEqual);
与 class 组件中 shouldComponentUpdate()方法不同的是,如果 props 相等,areEqual 会返回 true;如果 props 不相等,则返回 false。这与 shouldComponentUpdate 方法的返回值相反。
Children
map
React.Children.map(children, function[(thisArg)] )
<SafeAreaView style={style} edges={['right', 'bottom', 'left']}>
            {React.Children.map(children, child => child)}
 </SafeAreaView>
给props.children传参
React.Children.map(this.props.children, child => {
     return React.cloneElement(child, {
        params: () => {} // your props 
     });
 })
forEach
React.Children.forEach(children, function[(thisArg)])
与 React.Children.map()类似,但它不会返回一个数组。
count
React.Children.count(children)
返回 children中的组件总数量,等同于通过 map或 forEach调用回调函数的次数。
only
React.Children.only(children)
验证 children是否只有一个子节点(一个 React 元素),如果有则返回它,否则此方法会抛出错误。
注意: React.Children.only() 不接受 React.Children.map()的返回值,因为它是一个数组而并不是 React 元素。
toArray
React.Children.toArray(children)
将 children 这个复杂的数据结构以数组的方式扁平展开并返回,并为每个子节点分配一个 key。当你想要在渲染函数中操作子节点的集合时,它会非常实用,特别是当你想要在向下传递 this.props.children 之前对内容重新排序或获取子集时。
注意:
React.Children.toArray() 在拉平展开子节点列表时,更改 key 值以保留嵌套数组的语义。也就是说,toArray 会为返回数组中的每个 key 添加前缀,以使得每个元素 key 的范围都限定在此函数入参数组的对象内。
lazy
React.lazy() 允许你定义一个 动态加载的组件。这有助于缩减 bundle 的体积,并延迟加载在初次渲染时未用到的组件。
// 这个组件是动态加载的
const SomeComponent = React.lazy(() => import('./SomeComponent'));
请注意,渲染 lazy组件依赖该组件渲染树上层的 <React.Suspense>组件。这是指定加载指示器(loading indicator)的方式。
注意 : 使用 React.lazy 的动态引入特性需要 JS 环境支持 Promise。在 IE11 及以下版本的浏览器中需要通过引入 polyfill 来使用该特性。
Suspense
React.Suspense可以指定加载指示器(loading indicator),以防其组件树中的某些子组件尚未具备渲染条件。目前,懒加载组件是 <React.Suspense>支持的唯一
用例:
// 该组件是动态加载的
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
  return (
    // 显示 <Spinner> 组件直至 OtherComponent 加载完成
    <React.Suspense fallback={<Spinner />}>
      <div>
        <OtherComponent />
      </div>
    </React.Suspense>
  );
}
请注意,lazy 组件可以位于 Suspense 组件树的深处——它不必包装树中的每一个延迟加载组件。最佳实践是将 <Suspense> 置于你想展示加载指示器(loading indicator)的位置,而 lazy() 则可被放置于任何你想要做代码分割的地方。
猜你喜欢
- 2025-01-03 React 性能优化十大总结
- 2025-01-03 Vue和React的一些对比:哪个更适合你?
- 2025-01-03 React 的一些最佳安全实践
- 2025-01-03 搭建一个无需构建工具的 React 页面
- 2025-01-03 支持Vue、React混用的veaury库大火?前端摆脱二选一?
- 2025-01-03 前端开发react框架 - 组件
- 2025-01-03 从0到1无比流畅的React入门教程
- 2025-01-03 如何设计更优雅的 React 组件?
- 2025-01-03 React中使用Ant Table组件
- 2025-01-03 React支持了ES6 Classes,听听他们怎么说
欢迎 你 发表评论:
- 1576℃北京那些看上去很牛的车牌们!(北京厉害车牌)
- 1097℃2025年度视频去水印软件TOP5对比:哪款最值得用
- 663℃《我的世界》不同版本的差异 ——新手向
- 582℃新疆话里的“虫子”
- 537℃正畸治疗后的牙间黑三角
- 497℃蓝牙设备配对失败的系统性解决方案与技术解析
- 493℃中兴光猫 Telnet下设置大全(中兴光猫命令大全)
- 489℃未备份电脑文件数据恢复的七种方法
- 最近发表
- 标签列表
- 
- jdk (81)
- putty (66)
- rufus (78)
- 内网穿透 (89)
- okhttp (70)
- powertoys (74)
- windowsterminal (81)
- netcat (65)
- ghostscript (65)
- veracrypt (65)
- asp.netcore (70)
- wrk (67)
- aspose.words (80)
- itk (80)
- ajaxfileupload.js (66)
- sqlhelper (67)
- express.js (67)
- phpmailer (67)
- xjar (70)
- redisclient (78)
- wakeonlan (66)
- tinygo (85)
- startbbs (72)
- webftp (82)
- vsvim (79)
 

本文暂时没有评论,来添加一个吧(●'◡'●)