在 React.js 中使用 refs
在本文中,我们将探讨为什么 React.js 这个让你的代码远离 DOM 操作的库,会为你敞开大门。React 会根据组件的状态重新思考视图。它提供了 JSX(一种 JavaScript 上的语法糖)来设计视图层,然后修改 DOM 本身,而不是将控制权交给开发人员。
尽管如此,React 团队还是提供了逃生路线,并使库保持开放,以应对超出 React 设计范围的某些情况。
创建 refs
Refs 是逃生路线,最好尽可能避免使用它们。当我们使用 ref 获取 DOM 元素,然后修改其属性时,可能会与 React 的 diff 和 update 方法发生冲突。
让我们从一个简单的组件开始,并使用 ref 获取一个 DOM 元素,假设您已经知道如何设置一个基本的 react 应用程序。
import React, { useRef } from 'react'
function Button ({ label, action }) {
// declare & initializing a reference to null
const buttonRef = useRef(null)
// attaching 'buttonRef' to the <button> element in JSX
return (
<button onClick={action} ref={buttonRef}>{label}</button>
)
}
}
在上面的代码中,我们使用 React Hook 'useRef' 来创建并初始化一个名为 buttonRef 的变量。然后,我们将 buttonRef 赋值给 button JSX 元素上的 ref 属性。
使用 React refs
正如我们前面所讨论的,我们基于状态声明视图,虽然我们仍然使用函数来修改状态,但我们无法直接控制 DOM 的变化。不过,在某些情况下,在代码中引入 ref 是有意义的。
焦点控制
为了更好地理解问题陈述,让我们将情况描述成故事。
Arjun 是 Doogle 公司的软件开发实习生,他的经理给他布置了创建联系表单的任务。经理要求他在打开模态框时,重点关注表单中的第一个输入元素。Arjun 不知道如何在 React.js 中实现这一点。让我们来帮帮 Arjun。
import React, { useState } from "react";
const InputModal = ({ close }) => {
const [value, updateVal] = useState("");
const onChange = (e) => {
updateVal(e.target.value);
};
const onSubmit = (e) => {
e.preventDefault();
close();
};
return (
<div className="overlay">
<div className="modal">
<h1>Insert a new value</h1>
<form action="?" onSubmit={onSubmit}>
<input type="text" onChange={onChange} value={value} />
<button>Save new value</button>
</form>
</div>
</div>
);
};
export default InputModal;
我们需要做的第一件事是获取对输入的引用。
import React, { useState, useRef } from "react";
const InputModal = ({ close }) => {
const [value, updateVal] = useState("");
const inputRef = useRef(null);
const onChange = (e) => {
updateVal(e.target.value);
};
const onSubmit = (e) => {
e.preventDefault();
close();
};
return (
<div className="overlay">
<div className="modal">
<h1>Insert a value</h1>
<form action="?" onSubmit={onSubmit}>
<input type="text" onChange={onChange} value={value} ref={inputRef} />
<button>Save</button>
</form>
</div>
</div>
);
};
export default InputModal;
接下来,当我们的模式加载时,我们强制调用输入参考上的焦点。
import React, { useState, useRef, useEffect } from "react";
const InputModal = ({ close }) => {
const [value, updateVal] = useState("");
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
const onChange = (e) => {
updateVal(e.target.value);
};
const onSubmit = (e) => {
e.preventDefault();
close();
};
return (
<div className="overlay">
<div className="modal">
<h1>Insert a value</h1>
<form action="?" onSubmit={onSubmit}>
<input type="text" onChange={onChange} value={value} ref={inputRef} />
<button>Save</button>
</form>
</div>
</div>
);
};
export default InputModal;
注意:您需要通过声明的 ref 的当前属性来访问该元素。
点击此链接查看代码。尝试注释掉 inputRef 的实现,看看输入焦点在有 ref 和无 ref 的情况下是如何变化的。
检测是否包含元素
类似地,我们希望在事件触发时在应用中执行某个操作。比如,当用户点击模态框外部时关闭模态框。
import React, { useState, useRef, useEffect } from "react";
const InputModal = ({ close }) => {
const [value, updateVal] = useState("");
const inputRef = useRef(null);
const modalRef = useRef(null);
const onClickOverlay = (e) => {
const overlay = e.target;
if (modalRef.current && !modalRef.current.contains(overlay)) {
e.preventDefault();
e.stopPropagation();
close();
}
};
useEffect(() => {
inputRef.current.focus();
document.body.addEventListener("click", onClickOverlay);
}, []);
const onChange = (e) => {
updateVal(e.target.value);
};
const onSubmit = (e) => {
e.preventDefault();
close();
};
return (
<div className="overlay">
<div className="modal" ref={modalRef}>
<h1>Insert a value</h1>
<form action="?" onSubmit={onSubmit}>
<input type="text" onChange={onChange} value={value} ref={inputRef} />
<button>Save</button>
</form>
</div>
</div>
);
};
export default InputModal;
这里,我们检查用户点击是否超出了模态框引用限制。如果超出,我们将从 props 中调用 close() 函数来关闭模态框。
集成基于 DOM 的库
与 React 类似,在其生态系统之外,也有一些已使用多年的实用程序和库。要使用这些库,ref 就派上用场了。
GreenSock 库是动画示例的热门选择。要使用它,我们需要将 DOM 元素传递给它的任意方法。
让我们回到我们的模态框并添加一些动画
import React, { useState, useRef, useEffect } from "react";
import gsap from "gsap";
const InputModal = ({ close }) => {
const [value, updateVal] = useState("");
const inputRef = useRef(null);
const modalRef = useRef(null);
const overlayRef = useRef(null);
const onComplete = () => {
inputRef.current.focus();
};
const gaspTimeline = gsap.timeline({ paused: true, onComplete });
const onClickOverlay = (e) => {
const overlay = e.target;
if (modalRef.current && !modalRef.current.contains(overlay)) {
e.preventDefault();
e.stopPropagation();
close();
}
};
useEffect(() => {
//timeline - gasp
gaspTimeline
.from(overlayRef.current, {
duration: 0.25,
autoAlpha: 0
})
.from(modalRef.current, {
duration: 0.25,
autoAlpha: 0,
y: 25
});
gaspTimeline.play();
document.body.addEventListener("click", onClickOverlay);
}, []);
const onChange = (e) => {
updateVal(e.target.value);
};
const onSubmit = (e) => {
e.preventDefault();
close();
};
return (
<div className="overlay" ref={overlayRef}>
<div className="modal" ref={modalRef}>
<h1>Insert a value</h1>
<form action="?" onSubmit={onSubmit}>
<input type="text" onChange={onChange} value={value} ref={inputRef} />
<button>Save</button>
</form>
</div>
</div>
);
};
export default InputModal;
**这是工作演示。 **
转发 Refs
Refs 对于特定操作非常有用。这里展示的示例比我们通常在实际 Web 应用程序中看到的要简单一些。我们需要处理复杂的组件,并且很少直接使用纯 HTML 元素。在一个组件中使用另一个组件的 ref 是很常见的。
import React from 'react'
const LabelledInput = (props) => {
const { id, label, value, onChange } = props
return (
<div class="labelled--input">
<label for={id}>{label}</label>
<input id={id} onChange={onChange} value={value} />
</div>
)
}
export default LabelledInput
现在的问题是,将 ref 传递给该组件将返回其实例,即 React 组件引用,而不是像我们在第一个示例中那样我们想要关注的输入元素。
React 提供了forwardRef,它允许您在内部定义 ref 将指向哪个元素。
import React from 'react'
const LabelledInput = (props, ref) => {
const { id, label, value, onChange } = props
return (
<div class="labelled--input">
<label for={id}>{label}</label>
<input id={id} onChange={onChange} value={value} ref={ref}/>
</div>
)
}
export default React.forwardRef(LabelledInput)
现在,当父组件传递 ref 值时,它将获取输入,这有助于避免暴露组件的内部和属性并破坏其封装。
最初发布于amodshinde.com
文章来源:https://dev.to/amoled27/using-refs-in-reactjs-4oog