React ref的使用示例
写了一段时间的 react,99%都在写 state、prop、useState、useEffect,对 ref 特别不熟悉,前几天做一个需求,想用 ref 实现父组件捞子组件的某个状态值,结果失败了,特此整理一下 ref 相关内容。
什么是 ref
官网介绍
在典型的 React 数据流中,props 是父组件与子组件交互的唯一方式。要修改一个子组件,你需要使用新的 props 来重新渲染它。,在某些情况下,你需要在典型数据流之外强制修改子组件。被修改的子组件可能是一个 React 组件的实例,也可能是一个 DOM 元素。对于这两种情况,React 都提供了解决办法,即使用 ref 来获取 dom 或组件实例。
如何使用 ref
放在 dom 元素上
这是 ref 最直接的用法
export class Demo extends React.Component { constructor(props) { super(props) this.myRef = createRef() } ponentDidMount() { console.log(this.myRef) } render() { return <div ref={this.myRef}>测试</div> } }
打印看一下 ref 是啥
可以看出,ref.current 拿到了 dom 元素,所以我们可以实现 dom 元素本身的一些功能,如 input 的聚焦
export class Demo extends React.Component { constructor(props) { super(props) this.myRef = createRef() } onClick = () => { this.myRef.current.focus() } render() { return ( <div> <button onClick={this.onClick}>聚焦</button> <input ref={this.myRef} /> </div> ) } }
官网还提供了一种 ref 回调的形式:
export class Demo extends React.Component { constructor(props) { super(props) this.myRef = null } onClick = () => { this.myRef.focus() } render() { return ( <div> <button onClick={this.onClick}>聚焦</button> <input ref={ele => this.myRef = ele} /> // 这里的 ele 就是该 dom 元素 </div> ) } }
放在类组件上
其实组件跟原生 dom 差不多,也是拥有自己的 ui、一些功能的某种元素,所以将 ref 放在组件上,也可以获取到该组件的示例。
// 子组件 class Child extends React.Component { constructor(props) { super(props) this.state = { name: 'xx' } } render() { return <div>子元素{this.state.name}</div> } } export class Demo extends React.Component { constructor(props) { super(props) this.myRef = createRef() } ponentDidMount() { console.log(this.myRef) } render() { return ( <Child ref={this.myRef} /> ) } }
那既然可以获取到子组件的实例,我们就可以操作子组件了,比如文章最开始说,我想在父组件里去捞子组件的某些状态值。
class Child extends React.Component { constructor(props) { super(props) this.state = { count: 0 } } onClick = () => { this.setState({count: this.state.count+1}) } render() { return <button onClick={this.onClick}>点击+1{this.state.count}</button> } } export class Demo extends React.Component { constructor(props) { super(props) this.myRef = createRef() } onClick = () => { console.log(this.myRef.current.state.count) // 拿到子组件的状态值 } render() { return ( <div> <button onClick={this.onClick}>获取子组件的点击次数</button> <Child ref={this.myRef} /> // ref 获取到子组件实例 </div> ) } }
既然能拿值,我也能拿函数去修改子组件
class Child extends React.Component { constructor(props) { super(props) this.state = { name: 'xx' } } changeName = () => { this.setState({name: 'ww'}) } render() { return <div>子元素{this.state.name}</div> } } export class Demo extends React.Component { constructor(props) { super(props) this.myRef = createRef() } onClick = () => { this.myRef.current.changeName() // 父组件的手伸到子组件里去啦 } render() { return ( <div> <button onClick={this.onClick}>改变子组件的状态</button> <Child ref={this.myRef} /> </div> ) } }
这个例子并不恰当,父组件想更改子组件的状态的话,应该把状态提升到父组件中,然后作为子组件的props传递进去。
主要是 ref 提供一种方式去绕过 props 来实现父子组件通信。
放在函数组件上
这是我文章开头写需求时犯的错,ref 不能放在函数组件上,因为函数组件没有实例。
const Child = () => { return <div>子组件</div> } export const Demo = () => { const myRef = useRef() // 可以在函数组件内创建 ref useEffect(() => { console.log(myRef) }, []) return <Child ref={myRef} /> // 放在函数组件上无效 }
那函数组件就不能使用 ref 了吗,那肯定不是哈哈。我们可以使用 forwardRef 包装函数组件。
const Child = (props, ref) => { // 包装后,除了原有的 props 外, ref 也被传了进来 return <div ref={ref}>子组件</div> // 还是得挂载到 dom 上 } const ProChild = React.forwardRef(Child) // 重点在这里 export const Demo = () => { const myRef = useRef() useEffect(() => { console.log(myRef) }, []) return <ProChild ref={myRef} /> }
这里贴一下官网的 tip
那既然函数组件也可以使用 ref 的话,我们用函数组件实现一下父组件捞子组件的数据,不过可以看出,使用 forwardRef 包裹后,ref 还是得挂载到 dom 或者类组件上,如果我只想挂载数据还需要搭配 useImperativeHandle。
const Child = (props, ref) => { const [count, setCount] = useState(0) useImperativeHandle( ref, () => ({ // 这里就是暴露给外部 ref 的数据 getVal: ()=> count }), [count], ) const onClick = () => { setCount(pre => pre+1) } return <button onClick={onClick}>点击+1{count}</button> } const ProChild = React.forwardRef(Child) export const Demo = () => { const myRef = useRef() const onClick = () => { console.log(myRef.current.getVal()) // 拿到子组件的值 } return <><button onClick={onClick}>获取子组件的点击次数</button><ProChild ref={myRef} /></> }
至此完成了做需求时留下的问题 ✅
还是需要强调一下,父组件获取子组件状态的场景,一般还是状态提升 + 回调来通信,需求最终也是使用这种方式来实现的,最开始之所以想用 ref,是觉得状态提升后,子组件变化了会引起父组件的重新渲染,我只想拿数据而不引起渲染。
跟师傅说了一下我写需求时的想法,师傅见解如下
- 优先考虑状态提升
- 有性能问题的话,考虑状态提升 + memo
- 不想给多个组件加 memo 的话,就要考虑引入 redux/mobx 了
- 如果引入 redux/mobx 是一种成本的话,那 ref 也不是不可以哈哈哈
以上就是React ref的使用详解的详细内容,更多关于React ref的使用的资料请关注狼蚁SEO其它相关文章!
编程语言
- 甘肃哪有关键词排名优化购买方式有哪些
- 甘肃SEO如何做网站优化
- 河南seo关键词优化怎么做电话营销
- 北京SEO优化如何做QQ群营销
- 来宾百度关键词排名:提升您网站曝光率的关键
- 卢龙关键词优化:提升您网站排名的策略与技巧
- 山东网站优化的注意事项有哪些
- 四川整站优化怎样提升在搜索引擎中的排名
- 疏附整站优化:提升网站性能与用户体验的全新
- 海南seo主要做什么工作售后服务要做到哪些
- 荣昌百度网站优化:提升您网站的搜索引擎排名
- 河北seo网站排名关键词优化如何做SEO
- 江西优化关键词排名推广售后保障一般有哪些
- 古浪SEO优化:提升你的网站可见性
- 西藏网站排名优化怎么把网站排名在百度首页
- 如何提升阳东百度快照排名:详尽指南