component - ref
- 时钟案例
(1)受控组件
<script type="text/babel">
//受控组件
class Clock extends React.Component {
state = { time : new Date().toLocaleTimeString() }
render() {
return (
<div>
<h2>{this.state.time}</h2>
</div>
)
}
componentDidMount() {
setInterval(() => {
this.setState({
time : new Date().toLocaleTimeString()
})
},1000)
}
}
ReactDOM.render(<Clock />, document.getElementById("app"));
</script>
(2)非受控组件1(string)
<script type="text/babel">
//refs 引用容器
// 01 找到真实DOM容器
// this.refs = {} // 这个对象容器 => 用来存储真实DOM的引用
class Clock extends React.Component {
render() {
return (
<div>
{/*02 真实DOM放到this.refs={} 容器里
通过createElement 创建虚拟DOM,新增ref
ReactDOM.render 把虚拟DOM同步 渲染成真实DOM => 判断是否有ref
如果有ref:把ref属性对应的真实DOM h2 放进this.refs={}里面;
{ref属性对应的值 => key : 真实DOM h2} => {timeRef : 真实DOM h2}
*/}
<h2 ref='timeRef'>{new Date().toLocaleTimeString()}</h2>
</div>
)
}
componentDidMount() {
console.log(this.refs);
setInterval(() => {
//03 this.refs={} 容器里获取真实DOM => h2 => 进行操作
this.refs.timeRef.innerHTML = new Date().toLocaleTimeString()
},1000)
}
}
ReactDOM.render(<Clock />, document.getElementById("app"));
</script>
思考:
- 该案例是:实例组件.refs = {key : 真实DOM} key为ref属性对应的值(timeRef)
- 我们如何做到:实例组件.key = 真实DOM ?
(3)非受控组件2(function)
<script type="text/babel">
class Clock extends React.Component {
render() {
return (
<div>
{/*
通过createElement 创建虚拟DOM,新增ref属性
ReactDOM.render 把虚拟DOM同步 渲染成真实DOM => 判断是否有ref
如果有ref:判断ref属性对应的值是否为函数=>如果是:执行这个函数,把真实DOM作为参数传进去
组件实例.key = 真实DOM(element)
*/}
<h2 ref={e=>this.timeRef=e}>{new Date().toLocaleTimeString()}</h2>
<h2 ref={(element)=>{
this.timeRef2 = element
}}>{new Date().toLocaleTimeString()}</h2>
</div>
)
}
//组件挂载完毕:第一次渲染完毕 => 虚拟DOM已经完全转化成真实DOM
componentDidMount() {
console.log(this);
console.log(this.refs);
setInterval(() => {
// this.key = 真实DOM h2 => 进行操作
this.timeRef.innerHTML = new Date().toLocaleTimeString()
this.timeRef2.innerHTML = new Date().toLocaleTimeString()
},1000)
}
}
ReactDOM.render(<Clock />, document.getElementById("app"));
</script>
(4)非受控组件3(createRefs)
源码分析:
an immutable object with a single mutable value
function createRef() {
var refObject = {
current: null
};
{
Object.seal(refObject); // 对参数对象进行封闭 不能添加新的属性,只能有一个属性current
}
return refObject;
}
注意:
- 执行React.createRef() => 返回值{current:null}对象容器,但是这个返回值对象源码没有挂载到组件实例上,所以需要我们手动挂载 this.key = React.createRef()
<script type="text/babel">
class Clock extends React.Component {
constructor(props){
super(props)
this.timeRef = React.createRef() // {current: null}
}
render() {
return (
<div>
{/*02
通过createElement 创建虚拟DOM,新增ref属性
ReactDOM.render 把虚拟DOM同步 渲染成真实DOM => 判断是否有ref
如果有ref:会找到ref对应的值 => {current: null} 容器,把真实DOM h2 赋值给 容器current 属性
不需要给引用容器提供 key
*/}
<h2 ref={this.timeRef}>{new Date().toLocaleTimeString()}</h2>
</div>
)
}
//组件挂载完毕:第一次渲染完毕 => 虚拟DOM已经完全转化成真实DOM
componentDidMount() {
console.log(this);
console.log(this.timeRef);
setInterval(() => {
//03 this.key.current = 真实DOM h2 => 进行操作
this.timeRef.current.innerHTML = new Date().toLocaleTimeString()
},1000)
}
}
ReactDOM.render(<Clock />, document.getElementById("app"));
</script>
三种方案总结:
- ref = ‘key’ => this.refs = {} => {key : true DOM}
- ref = {e=>this.key = e} => this.key = true DOM
- this.key = React.createRef(); {current:null} = {current : true DOM}
- ref-form (form表单收集)
(1)ref = ‘key’
<script type="text/babel">
class Form extends React.Component {
handleClick = ()=>{
console.log(this.refs);
console.log(this.refs.inputRef);
console.log(this.refs.inputRef.value);
}
render() {
return (
<div>
<input type="text" ref='inputRef' />
<button onClick={this.handleClick}>commit</button>
</div>
)
}
}
ReactDOM.render(<Form />, document.getElementById("app"));
</script>
(2)ref = {e=>this.属性 = e}
<script type="text/babel">
class Form extends React.Component {
handleClick = ()=>{
console.log(this.inputRef);
console.log(this.inputRef.value);
}
render() {
return (
<div>
<input type="text" ref={e=>this.inputRef = e} />
<button onClick={this.handleClick}>commit</button>
</div>
)
}
}
ReactDOM.render(<Form />, document.getElementById("app"));
</script>
(3)ref = {this.属性}
<script type="text/babel">
class Form extends React.Component {
constructor(){
super()
this.inputRef = React.createRef()
}
handleClick = ()=>{
console.log(this.inputRef);
console.log(this.inputRef.current);
console.log(this.inputRef.current.value);
}
render() {
return (
<div>
<input type="text" ref={this.inputRef} />
<button onClick={this.handleClick}>commit</button>
</div>
)
}
}
ReactDOM.render(<Form />, document.getElementById("app"));
</script>
(4)绑定事件
<script type="text/babel">
class Form extends React.Component {
handleChange = (e)=>{
console.log(e.target.value);
}
render() {
return (
<div>
<input type="text" onChange={this.handleChange} />
</div>
)
}
}
ReactDOM.render(<Form />, document.getElementById("app"));
</script>
3.form 案例
- 失去焦点 => 触发事件 => 弹出input value
<script type="text/babel">
//失去焦点 => 触发事件 => 弹出input value
class Form extends React.Component {
handleBlur = (e)=>{
console.log(e.target)
console.log(e.target.value);
alert(e.target.value)
}
render() {
return (
<div>
<input type="text" onBlur={this.handleBlur} />
<button onClick={this.handleClick}>commit</button>
</div>
)
}
}
ReactDOM.render(<Form />, document.getElementById("app"));
</script>
- 点击 => 触发事件 => 弹出input value
<script type="text/babel">
class Form extends React.Component {
constructor() {
super()
this.inputRef =React.createRef()
}
handleClick = ()=>{
alert(this.inputRef.current.value)
}
render() {
return (
<div>
<input type="text" ref={this.inputRef} />
<button onClick={this.handleClick}>commit</button>
</div>
)
}
}
ReactDOM.render(<Form />, document.getElementById("app"));
</script>
- 获取焦点
<script type="text/babel">
class Form extends React.Component {
constructor() {
super()
this.inputRef =React.createRef()
}
handleClick = ()=>{
// 点击按钮获取焦点
this.inputRef.current.focus()
}
render() {
return (
<div>
<input type="text" ref={this.inputRef} />
<button onClick={this.handleClick}>commit</button>
</div>
)
}
//已经渲染完毕 可以直接使用真实DOM
componentDidMount() {
//自动获取焦点
this.inputRef.current.focus()
}
}
ReactDOM.render(<Form />, document.getElementById("app"));
</script>
4.ref-forward
- 类组件:
<script type='text/babel'>
class ChildInput extends React.Component {
render() {
return (
<div>
<h2>child input</h2>
<input type="text" ref={this.props.callbackRef} />
</div>
)
}
}
// 需求:获取某个子组件里面的 具体某个DOM
// 回调函数方式:给子组件传递一个回调函数callback,子组件通过ref = {callback},把组件的具体DOM挂载到父组件属性
class FatherForm extends React.Component {
callbackRef = (e) => {
this.inputRef = e
}
handleClick = () => {
console.log(this);
this.inputRef.focus()
}
render() {
return (
<div>
<ChildInput callbackRef={this.callbackRef} />
<button onClick={this.handleClick}>commit</button>
</div>
)
}
}
ReactDOM.render(<FatherForm />, document.getElementById('app'))
</script>
- 函数组件:
<script type='text/babel'>
function ChildInput (props) {
return (
<div>
<h2>child input</h2>
<input type="text" ref={props.callbackRef} />
</div>
)
}
class FatherForm extends React.Component {
callbackRef = (e) => {
this.inputRef = e
}
handleClick = () => {
console.log(this);
this.inputRef.focus()
}
render() {
return (
<div>
<ChildInput callbackRef={this.callbackRef} />
<button onClick={this.handleClick}>commit</button>
</div>
)
}
}
ReactDOM.render(<FatherForm />, document.getElementById('app'))
</script>
- forward(转发)方式
<script type='text/babel'>
function ChildInput(props ,ref) {
return (
<div>
<h2>child input</h2>
<input type="text" ref={ref} /> {/*地址 => {current:null} => {current:input}*/}
</div>
)
}
// 引用转发:引用地址的转发,自动传递ref的一种技术
const RefChildInput = React.forwardRef(ChildInput)
class FatherForm extends React.Component {
constructor() {
super()
this.inputRef = React.createRef()
}
callbackRef = (e) => {
this.inputRef = e
}
handleClick = () => {
console.log(this);
this.inputRef.current.focus()
}
render() {
return (
<div>
<RefChildInput ref={this.inputRef} />
<button onClick={this.handleClick}>commit</button>
</div>
)
}
}
ReactDOM.render(<FatherForm />, document.getElementById('app'))
</script>