refs&dom樣例詳解
import * as React from 'react' import * as ReactDOM from 'react-dom/client'; import {createPortal} from 'react-dom' const root = ReactDOM.createRoot(document.getElementById('root')) // const CustomInput = React.forwardRef((props,ref)=>{ // return ( // <div> // <input /> // </div> // ) // }) //你不能在函數組件上使用ref屬性 因為他們沒有實例 //但是你可以通過轉發的形式 來給函數組件添加ref屬性 class CustomInput extends React.Component{ constructor(props){ super(props); } open(){ alert(123) } render(){ return ( <div> <input /> </div> ) } } // const CustomInput = (props)=>{ // return ( // <div> // <input /> // </div> // ) // } //在特殊情況下你可能需要將子組件的dom暴漏給父組件 //16.3以上的版本我們可以通過轉發 //16.3以下的版本 我們可以使用將refs作為一個特殊的props向下傳遞 或者使用回調refs //不同于傳遞createref創建的ref屬性 //refs回調函數 接受react組件實例或者是dom元素作為參數,使它能夠在其他地方被存儲和訪問 //如果refs回調函數是以內聯函數定義的,那么他會被執行二次一次是傳null 第二次才是傳遞真正的dom元素 //因為組件重新渲染會生成一個新的函數實例,react會清空舊的ref并設置新的ref //我們可以通過像綁定class函數那樣的形式 去設置refs回調 就不會出現上述問題了 class App extends React.Component{ constructor(props){ super(props); this.inputRef = null; //此回調接受一個組件實例或者是dom元素作為參數 this.refsCallback = (el)=>{ console.log(el) this.inputRef = el; } this.handleClick = this.handleClick.bind(this); } handleClick(){ this.inputRef.open() } componentDidMount(){ console.log(this.inputRef) } render(){ return ( <div> <CustomInput ref = {this.refsCallback } /> <button onClick={this.handleClick}>點擊我文本框獲取焦點</button> </div> ) } } //不管怎樣你可以在函數內部使用ref屬性 指定一個dom或者是class組件 // function App(){ // const textInput = React.useRef(null); // function handleClick(){ // textInput.current.focus(); // } // return ( // <div> // <CustomInput forwardRef={textInput} /> // <button onClick={handleClick}>點擊我文本框獲取焦點</button> // </div> // ) // } root.render( <App /> );
refs總結:
1、refs作用于html元素,構造組件時 創建的ref對象 會接收底層的dom節點作為其current屬性
2、refs作用于組件,構造組件時 創建的ref對象 會接收組件掛載的實例作為其current屬性
3、不能為函數式組件指定ref屬性,因為函數式組件沒有實例(可以通過轉發ref的方式 轉發給函數式組件)
創建refs的三種方式:
1、字符串ref(已棄用)
2、createRef api創建ref
3、回調式ref
import React, { Children, useState, useTransition,Profiler,useRef,forwardRef,useImperativeHandle} from 'react';
import ReactDOM from 'react-dom/client';
import { createPortal } from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
//拷貝所有非react的靜態方法
import hoistNonReactStatic from 'hoist-non-react-statics';
const root = ReactDOM.createRoot(document.getElementById('root'));
//ref屬性作用于html元素時 構造函數中創建的ref 會接收底層的dom元素作為其current屬性
//ref屬性作用于組件時 構造函數中創建的ref 會接收掛載的組件實例作為其current屬性
//函數組件不能接收ref屬性 因為函數組件沒有實例
//將ref附加到react元素上
//如果對子組件的選擇沒有控制權的話,你可以直接使用findDomNode 但是已經被禁用了 避免使用他
class CustomTextInput extends React.Component{
constructor(props){
super(props);
this.inputRef = React.createRef();
this.handleFoucs = this.handleFoucs.bind(this);
}
handleFoucs(){
console.log('走這里了')
this.inputRef.current.focus();
}
render(){
return (
<form>
<label>
選中框1:
<input ref={this.inputRef} />
</label>
</form>
)
}
}
function CustomTextInput2(props,ref){
//函數式組件內部使用ref
const domRef = useRef();
useImperativeHandle(
ref,
() => {
return {
getDom:()=>{
console.log(domRef.current)
}
}
},
[]
)
return (
<div ref={domRef}>測試</div>
)
}
CustomTextInput2 = forwardRef(CustomTextInput2)
class CustomTextInput3 extends React.Component{
constructor(props){
super(props)
}
render(){
return null;
}
}
//componentDidMount componentDidUpdate 在這二個生命周期觸發之前 react會保證refs一定是最新的
class AutoFocusTextInput extends React.Component{
constructor(props){
super(props);
this.inputExample = React.createRef();
//第一步:創建ref 并將ref分配給組件的實例屬性 這樣就可以在全局引用他了
//第二步:通過ref屬性 將創建ref附加到react元素上 (附加的元素有可能是htmldom節點或者是react組件)
//第三步:組件在掛載時 會將對應的react元素傳入到 創建的ref的current屬性上 會在componentDidMount componentDidUpdate生命周期觸發之前更新
this.tempRef = React.createRef();
//ref回調
//通過實例屬性 來存儲組件的實例或者是dom元素
this.aa = null;
this.callFn = elem=>{
this.aa = elem;
}
}
componentDidMount(){
this.inputExample.current.handleFoucs();
console.log(this.aa)
}
render(){
return (
// 這個組件必須是class組件 不能在函數組件中使用ref屬性 因為函數式組件沒有實例
//不管怎樣 你可以在函數組件的內部使用ref屬性
<>
<CustomTextInput ref={this.inputExample} />
<CustomTextInput2 ref={this.tempRef} />
<CustomTextInput3 ref={this.callFn} />
</>
)
}
}
function P2(props){
return (
<div ref={props.inputRef}>
王武2
</div>
)
}
class P1 extends React.Component{
constructor(props){
super(props);
this.el = null;
//使用class 綁定函數的方式 解決此問題
this.inputFn = (el)=>{
console.log('這里只走一次')
this.el = el
}
}
componentDidMount(){
console.log(this.el)
}
render(){
// <P2 inputRef={(el)=>{
// // 內聯的ref回調 等到更新時會被調用二次第一次傳null 第二次傳入dom元素
// // 內聯的每次更新時都會創建新的函數實例,會清空舊的ref并設置新的ref
// console.log('內聯的每次更新時都會創建新的函數實例,會清空舊的ref并設置新的ref')
// console.log(el)
// this.iref = el
// }} />
return (
<P2 inputRef={this.inputFn} />
)
}
}
class Apps extends React.Component{
constructor(props){
super(props)
//創建refs
//并通過ref屬性 附加到react元素當中
this.input = React.createRef();
this.state = {
count:1
};
//為什么綁定this
//原因1:react組件的class與普通的class的編寫一樣 里面的方法不會自動綁定this到實例上 這與js的運行時環境有關系
//原因2:當class中的方法 被賦值給一個變量使用或者是當做回調函數使用時 方法里面的this也不會自動綁定到實例上 所以需要手動綁定
//綁定的方法:bind 或者箭頭函數 因為箭頭函數有自己的詞法環境 this會被靜態的綁定
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
this.setState((state)=>({
count:state.count + 1
}))
}
componentDidMount(){
//會在組件掛載時給current屬性,傳入dom元素 并在卸載時給current屬性傳入null
//ref會在 componentDidMount 和 componentDidUpdate 生命周期鉤子觸發之前更新
// this.input.current.focus()
// console.log(this.input)
}
render(){
return (
<>
<button onClick={this.handleClick}>{this.state.count}</button>
<AutoFocusTextInput />
<P1 />
</>
)
}
}
root.render(<Apps />)
//父組件訪問子組件中dom元素的節點 有一下方法
//1、refs轉發
//2、通過一個特殊的props傳遞下去
//3、回調ref
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

浙公網安備 33010602011771號