咱们知道react数据流是自上而下的,想要改变子组件的状态只须要传递props,可是在某些特定场景下,咱们想获取子组件实例或者dom元素,按咱们本身的方式来操做元素的行为,这个时候就须要用到ref来获取子组件或者dom元素html
场景: 动画 或者 某个时候咱们 想focus 一个 input 输入框node
1.能够在dom元素上面使用ref属性react
2.能够在class组件上面使用ref属性git
3.不许在函数组件上面使用ref属性,缘由:函数组件没有实例,父组件获取子组件的ref,实际上是在获取子组件的实例;(可是能够在函数组件中 获取子组件实例的ref,并用一个变量存储起来,这个是没有问题的)github
我的感受函数组件仍是有不少限制的,好比不能使用state和生命周期方法,也不能使用ref属性(这个问题我在开发中遇到了,详情见下文)api
class App extends React.Component {
constructor() {
super();
this.inputRef = React.createRef();
}
handleClick = () => {
this.inputRef.current.focus();
};
render() {
return (
<div className="App"> <button onClick={this.handleClick}>点我 进行focus</button> <input ref={this.inputRef} /> </div> ); } } 复制代码
此处示例是react16.3+版本的使用(若是想使用旧版本请看下文),步骤有三个:数组
1.createRef方法 生成ref 对象app
2.render的时候 接收 子组件或者dom元素的ref属性less
3.用this.inputRef.current 来获取这个 子组件或者dom元素dom
react16.2及如下通常用它
componentDidMount() {
if (this.img && this.img.complete) { //此处能够获取到 img 这个dom 元素
this.onLoad()
}
}
render() {
const {className, src, alt, onClick, onRef, preview} = this.props
return (
{preview && !src ? (
<div> <DefaultImageIcon /> </div>
) : (
<img ref={node => { this.img = node if (onRef) { onRef() } }} src={src} alt={alt} onLoad={this.onLoad} onClick={onClick} /> )} ) } 复制代码
能够看到, 咱们也能够直接使用回调的方式来获取ref,其实我的感受这种方式更简单,可是至于做者为何放弃了这种方式,我目前还不清楚。~我以为是由于跨组件传递很麻烦吧~。 咱们还看到,代码中,onRef方法能够直接把 ref传给上层组件,那么,若是想使用 新版本api将 ref传递给上层组件,有什么办法呢? 请看 forwardRef
const MyInput = React.forwardRef((props, ref) => (
<div>
我是 自定义input 组件
<input ref={ref}/>
</div>
))
class App extends React.Component {
constructor() {
super();
this.inputRef = React.createRef();
}
handleClick = () => {
this.inputRef.current.focus();
};
render() {
return (
<div className="App">
<button onClick={this.handleClick}>点我 进行focus</button>
<MyInput ref={this.inputRef}/>
</div>
);
}
}
复制代码
若是想在 上层组件中获取 下层组件内部的 dom 元素或者子孙组件实例,那么可使用 React.forwardRef 给组件包裹一层. 执行顺序是: 上层组件 createRef => App 组件传递 ref 给 MyInput => MyInput 经过 forwardRef 方法 接收到 ref => input 输入框 将节点 传给ref(此时这个ref已是上层组件的ref了)
其实 forwardRef 使用到的场景很少, 可是有时候 确实须要跨组件传递 ref, 那就只能经过它来实现了。 咱们知道, 为了实现一些逻辑,咱们会使用高阶组件,它其实就是给一个组件封装一层,可是若是咱们想在上层组件获取ref,获取的是高阶组件的ref, 为何?举个 例子: 最上层组件 叫 App, 高阶组件叫 MyHOC, 子组件叫 MyInput, 咱们想在App 中 获取 这个MyInput, 那么以下使用是错误的:
以下例子,App里面接收到ref 实际上是Foo 的ref,然而 Foo 没有ref
// MyInput.js
import MyHOC from './MyHOC'
const MyInput = () => (
<div> 我是 自定义input 组件 <input /> </div>
)
return MyHOC(MyInput)
// MyHOC.js
const MyHOC = (WrappedComponent) => {
// do sth here..
class Foo extends React.Component {
render() {
return <WrappedComponent {...this.props} />;
}
}
return <Foo />;
}
export default MyHOC
//App.js
import MyInput from './MyInput';
const ref = React.createRef();
<MyInput
ref={ref} // 此处的 ref实际上是 Foo
/>;
复制代码
那么应该如何正确地在Hoc中使用呢?
// MyInput.js
import MyHOC from './MyHOC'
const MyInput = () => (
<div> 我是 自定义input 组件 <input /> </div>
)
return MyHOC(MyInput)
// MyHOC
const MyHOC = (WrappedComponent) => {
class Foo extends React.Component {
render() {
const { forwardedRef, ...rest } = this.props;
// 此时 ref 即是这个WrappedComponent
return <WrappedComponent ref={forwardedRef} {...rest} />;
}
}
// 用React.forwardRef 包裹一层,这样将 上层组件的ref 命名为 forwardedRef (任意定义的一个属性名) 传递给子组件
return React.forwardRef((props, ref) => {
return <Foo {...props} forwardedRef={ref} />;
});
}
export default MyHOC
//App.js
import MyInput from './MyInput';
const ref = React.createRef();
<MyInput
ref={ref}
/>;
复制代码
如上代码所示, 惟一一个不一样的地方是: MyHOC
组件使用 React.forwardRef
将ref 向下层传递, 这样外层组件接收到的ref 就是真实的 MyInput 组件,而非 Foo了
<Form.Item
label={field.label}
key={`key_${field.label}`}
>
{render
? render()
: getFieldDecorator(field.fieldName, {
rules: R.pathOr([], ['config', 'rules'], field),
initialValue: data[field.field],
})(<Item {...field}/>)} // 此处的Item是个 函数式组件, 这样写会出错 // })(Item(field))} 这样写才是ok的, 由于Item会返回一个类组件 </Form.Item> // 报错信息: Warning: Stateless function components cannot be given refs. Attempts to access this ref will fail. 复制代码
null
, ref 会在 componentDidMount
或者 componentDidUpdate
以前更新