React 高级API用法 Context&Refs转发
1、Context
前言
react 中组件数据传递是通过props属性 单向传递的,但当组件层级过多时组件数据传递会变繁琐。Context允许数据隔代传递,而不必显式的通过组件树逐层传递props。从而使局部数据“共享”。
使用场景
- 父组件用Provider生产数据,子组件使用Consumer消费数据
- 父组件用Provider生产数据,组件使用ContextType接收数据(只用于类组件)
- 动态和静态Context(父组件更新Context,被Provider包裹的子组件刷新数据,没被Provider包裹的子组件使用Context默认值)
- 在嵌套组件中更新Context(子组件通过Context传递的函数更新数据)
- 消费多个Context (createContext多个Context对象父组件通过Provider 层层嵌套子组件,子组件通过Consumer 层层嵌套取数据)
ProductContext.js:创建的Context组件文件
ProviderPage.js:父组件(数据生产者)
MiddlePage.js:中间组件
ConsumerPage.js:子组件(数据消费者)
GrandSonPage.js:子组件(数据消费者)
场景1:父组件用Provider生产数据,子组件使用Consumer消费数据
src\page\context\part_1\ProviderPage.js
场景2:父组件用Provider生产数据,组件使用ContextType接收数据(只用于类组件 因为需要使用static)
作用:contextType 可以简化 context 的使用,不使用 consumer 也可以共享变量
src\page\context\part_2\ProviderPage.js
场景3:动态和静态Context(父组件更新Context,被Provider包裹的子组件刷新数据,没被Provider包裹的子组件使用Context默认值)
作用:contextType 可以简化 context 的使用,不使用 consumer 也可以共享变量
src\page\context\part_3\ProviderPage.js
场景4:在嵌套组件中更新Context(子组件通过Context传递的函数更新数据)
子组件通过调用父组件context中的函数 通知父组件更新状态 从而达到子传父组的效果
src\page\context\part_4\ProviderPage.js
场景5:消费多个Context (createContext多个Context对象父组件通过Provider 层层嵌套子组件,子组件通过Consumer 层层嵌套取数据)
父组件中通过多层嵌套 将context 传入子组件 使数据更有层次
2、Refs转发
什么是refs
Refs 是一个 获取 DOM节点或 React元素实例的工具。在 React 中 Refs 提供了一种方式,允许用户访问DOM 节点或者在render方法中创建的React元素。
创建 Refs
1、React.createRef() 【React >=16.3】
这种方式既可以在函数组件中使用,也可以在class组件中使用。
class App extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
componentDidMount() {
console.log(this.myRef)
console.log(this.myRef.current)
}
render() {
return <div ref={this.myRef}>React.createRef()</div>
}
}
2、React.useRef() 【React >=16.8】
这种只可以在函数组件中使用。
const App = ()=>{
const myRef = React.useRef(null)
//const myRef = React.createRef() 两种创建 ref 对象的方式都可以
React.useEffect(()=>{
console.log(myRef)
console.log(myRef.current)
},[]) //模拟生命周期
return (
<div ref={myRef}>React.useRef()</div>
)
}
3、回调 Refs
const App = () => {
// 初始化定义ref
let myRef = null;
// 初始化定义ref
React.useEffect(() => {
console.log(myRef)
console.log(myRef.current)
}, []) //模拟生命周期
return (
<div ref={(ref) => myRef = ref} >回调 Refs</div>
)
}
class App extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log(this.myRef)
console.log(this.myRef.current)
}
render() {
return <div ref={ref => this.myRef = ref}>React.createRef()</div>
}
}
- 当 ref 属性用于 HTML 元素时,构造函数中使用 React.createRef() 创建的 ref 接收底DOM 元素作为其 current 属性。
- 当 ref 属性用于 class 组件时,ref 对象的current属性为组件实例对象 。
- 函数组件无法使用 ref 属性,因为他们没有实例。
Refs转发应用场景
解决获取子组件通内部的 DOM 元素/方法.
const FancyButton = React.forwardRef((props, ref) => {
let myRef
useImperativeHandle(ref, () => {
return {
func
};
})
function func() {
console.log('执行我', myRef);
}
return <button ref={ref => myRef = ref} className='FancyButton' > {props.children}</button >
})
function App() {
let myHandle
React.useEffect(() => {
// useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。
console.log(myHandle)
console.log(myHandle.func())
}, []) //模拟生命周期
return (
<FancyButton ref={handle => myHandle = handle} >Click me</FancyButton>
)
}
使用props自定义onRef/onRefDom属性 用于获取子组件方法或属性
缺点:如果一个组件被多次使用,正常情况下想要调用其组件内的方法需要传入props来调用,每次传入的话就比较多余。
class Child extends React.Component {
constructor(props) {
super(props);
this.props.onRef && this.props.onRef(this);
}
func() {
console.log("执行我")
}
render() {
return (<div ref={node => this.props.onRefDom(node)}>子组件</div>);
}
}
class App extends React.Component {
handleOnClick = () => {
}
componentDidMount() {
console.log(this.Child)
console.log(this.ChildDom)
this.Child.func();
}
render() {
return (<div>
<button onClick={this.handleOnClick}>click</button>
<Child onRef={node => this.Child = node} onRefDom={node => this.ChildDom = node}></Child>
</div>);
}
}