导航
24Ref/App_1input.jsx
class MyInput extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
state = {
inputValue: ''
}
inputFocus() {
const oInput = this.inputRef.current;
oInput.focus()
this.setState({
inputValue: ''
})
}
changeInputValue(e) {
this.setState({
inputValue: e.target.value
})
}
render() {
return (
<div>
<input type="text"
ref={ this.inputRef }
value={ this.state.inputValue }
onChange={ this.changeInputValue.bind(this) }
/>
<button onClick={ this.inputFocus.bind(this) }>清空并聚焦</button>
<br />
<p>{ this.state.inputValue }</p>
</div>
)
}
}
class App extends React.Component {
render() {
return (
<MyInput />
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
通过 props.onRef
App_5modal_dialog.jsx 01
class Modal extends React.Component {
constructor(props) {
super(props);
this.modalRef = React.createRef();
// 把自身传递给外界
if (props.onRef) {
props.onRef(this)
}
}
open() {
this.modalRef.current.style.display = 'block';
}
close() {
this.modalRef.current.style.display = 'none';
}
render() {
return (
<div
ref={ this.modalRef }
style={{
width: '300px',
height: '300px',
border: '1px solid #000',
display: 'none'
}}
>
<h1>modal dialog</h1>
<p>This is a modal dialog</p>
</div>
)
}
}
class App extends React.Component {
modalOpen(status) {
switch (status) {
case 'open':
this.modal.open();
break;
case 'close':
this.modal.close();
break;
default:
break;
}
}
render() {
return (
<div>
{ /* render 执行到下面代码时,把 instance 组件自身赋值给了 App 组件实例的 modal 属性 } */ }
<Modal onRef={ instance => this.modal = instance } />
<div>
<button onClick={ this.modalOpen.bind(this, 'open') }>Open</button>
<button onClick={ this.modalOpen.bind(this, 'close') }>Close</button>
</div>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
或者
class Demo extends Component {
inputRef = null
render() {
return (
<input ref={ c => this.inputRef = c } />
)
}
}
当然除此以外,我们还可以利用 状态提升 而不是 refs 来将 modal 打开与否放进 App 中:
App_5modal_dialog.jsx 02
class Modal extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div
style={{
width: '300px',
height: '300px',
border: '1px solid #000',
display: this.props.isOpen ? 'block' : 'none'
}}
>
<h1>modal dialog</h1>
<p>This is a modal dialog</p>
</div>
)
}
}
class App extends React.Component {
state = {
isOpen: false
}
modalOpen(status) {
this.setState({
isOpen: status === 'open'
})
}
render() {
return (
<div>
<Modal isOpen={ this.state.isOpen } />
<div>
<button onClick={ this.modalOpen.bind(this, 'open') }>Open</button>
<button onClick={ this.modalOpen.bind(this, 'close') }>Close</button>
</div>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
App_6ref.jsx 01
class Test extends React.Component {
constructor(props) {
super(props);
this.divRef = React.createRef();
console.log('Test constructor', this.divRef); // log日志未把对象展开时为null,展开后未 Ref 对象(因为此刻 log 的瞬间,ref 是没值的,componentDidMount 时才有值)
}
componentDidMount() {
console.log('Test componentDidMount', this.divRef); // 更新,有值
}
componentDidUpdate() {
console.log('Test componentDidUpdate', this.divRef); // 更新,有值
}
render() {
return (
// 1. 绑定到元素上,ref 的 current 是元素
<div ref={ this.divRef }>
{ this.props.children }
</div>
)
}
}
App_6ref.jsx 02
function Test2() {
// 3. 函数组件中,通过 hooks 使用 ref
const divRef = React.useRef(null);
React.useEffect(() => {
console.log('函数组件', divRef);
}, []);
return (
<div ref={ divRef }>Hello, Function ref</div>
)
}
class App extends React.Component {
state = {
text: 'This is a Test component'
}
constructor(props) {
super(props);
this.testRef = React.createRef();
}
componentDidMount() {
console.log('App componentDidMount', this.testRef);
setTimeout(() => {
this.setState({
text: 'change text'
})
}, 1000);
}
render() {
return (
<div>
{/* 2. 绑定到类组件上,ref 的 current 是组件实例 */}
<Test ref={ this.testRef }>This is a Test component</Test>
<Test2 />
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
App_7ref转发机制.jsx
使用条件:React 16.3 及其以上版本
当 ref 赋值给一个 React 组件时,ref 的 current 是组件实例,此刻拿不到组件内部的 DOM 元素,如果需要拿到,就可以利用 Refs 的转发机制
React 16.3 及其以上版本 可以使用 Refs 转发,将 ref 自动的通过组件传递给子组件。
// class MyInput extends React.Component {
// render() {
// return (
// <input type="text" placeholder="请填写..." />
// )
// }
// }
// 3. 通过 forwardRef 向 input 转发 ref 属性
// React.forwardRef((props, ref) => { return React元素 })
const MyInput = React.forwardRef((props, ref) => {
// 5. ref 只能用 forwardRef 定义的组件接收
return <input type="text" placeholder={ props.placeholder } ref={ ref } />
});
class App extends React.Component {
constructor(props) {
super(props)
// 1. 创建 ref 对象
this.myInputRef = React.createRef();
}
componentDidMount() {
// 4. myInputRef.current 指向了 input DOM
console.log(this.myInputRef);
}
inputOperate() {
const oInput = this.myInputRef.current;
oInput.value = ''
oInput.focus()
}
render() {
return (
<div>
{/* 2. 给组件赋值 ref */}
<MyInput ref={ this.myInputRef } placeholder="请填写..." />
<button onClick={ this.inputOperate.bind(this) }>Click</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
App_8ref在高阶组件中的应用.jsx
class MyInput extends React.Component {
render() {
return (
<input type="text" placeholder={ this.props.placeholder } />
)
}
}
function InputHOC(WrapperComponent) {
class Input extends React.Component {
render() {
// 容器组件内部获取 ref 属性
const { forwardRef, ...props } = this.props
// 将 forwardRef 传递给参数组件
return <WrapperComponent ref={ forwardRef } { ...props } />
}
}
// 向子组件传递 ref
function forwardRef(props, ref) {
return <Input { ...props } forwardRef={ ref } />
}
forwardRef.displayName = 'Input - ' + WrapperComponent.name
return React.forwardRef(forwardRef)
}
const MyInputHOC = InputHOC(MyInput);
class App extends React.Component {
constructor(props) {
super(props)
this.inputRef = React.createRef()
}
componentDidMount() {
console.log(this.inputRef)
}
render() {
return (
// 用 ref 接收我们的转发的 ref
<MyInputHOC ref={ this.inputRef } placeholder="请填写..." />
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
App_9ref旧版本转发.jsx
// React16.2 及其以下 ref 传递方式
// 1. props
// class MyInput extends React.Component {
// render() {
// return (
// <input type="text" ref={ this.props.inputRef } />
// )
// }
// }
// class App extends React.Component {
// constructor(props) {
// super(props)
// this.inputRef = React.createRef()
// }
// componentDidMount() {
// console.log(this.inputRef); // current 是 input DOM
// }
// render() {
// return (
// <MyInput inputRef={ this.inputRef } />
// )
// }
// }
// ReactDOM.render(<App />, document.getElementById('app'))
// 2. 回调1: 本组件想用的回调写法
// class MyInput extends React.Component {
// constructor(props) {
// super(props);
// this.MyInput = null;
// }
// setMyInput(el) { // 回调中的 el 就是 input 节点
// this.MyInput = el;
// }
// focusInput() {
// this.MyInput.value = ''
// this.MyInput.focus()
// }
// render() {
// return (
// <>
// <input type="text" ref={ this.setMyInput.bind(this) } />
// <button onClick={ this.focusInput.bind(this) }>Click</button>
// </>
// )
// }
// }
// class App extends React.Component {
// render() {
// return (
// <MyInput />
// )
// }
// }
// ReactDOM.render(<App />, document.getElementById('app'))
// 3. 回调2: 父组件想用的回调写法
class MyInput extends React.Component {
render() {
return (
<>
<input type="text" ref={ this.props.inputRef } />
</>
)
}
}
class App extends React.Component {
constructor(props) {
super(props)
this.oInput = null
}
componentDidMount() {
console.log(this.oInput)
}
render() {
return (
<MyInput inputRef={ el => this.oInput = el } />
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
