开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情
接着上一篇的内容,今天我们复习React基础之Ref,Context
Ref
使用ref我们可以操作DOM元素或组件。
如何创建ref对象:
React.createRef()
该方法只能在类组件中使用
比如点击按钮获取某个DOM元素
class Child extends React.Component {
constructor() {
super();
this.divRef = React.createRef();
this.state = {
books: "三国演义",
age: "20"
};
}
showDivRef = () => {
console.log(this.divRef.current); // <div id="book">Jack给的书:三国演义 </div>
};
render() {
return (
<div>
<div ref={this.divRef} id="book">
Jack给的书:{this.state.books}
</div>
<button onClick={this.showDivRef}>展示Ref</button>
</div>
);
}
}
useRef()
该hook是针对函数组件使用的,类组件使用React.createRef()。
比如获取某个组件
function FnChild() {
const currentDom = React.useRef(null);
const getRef = () => {
console.log(currentDom.current);
};
return (
<div>
<p>我是函数组件</p>
<Child ref={currentDom}></Child>
<button onClick={getRef}>按钮2</button>
</div>
);
}
打印结果:
⚠️React.createRef()也可以在函数组件中使用。
只不过React.createRef创建的引用不能保证每次重新渲染后引用固定不变。如果你只是使用React.createRef“勾住”JSX组件转换后对应的真实DOM对象是没问题的,但是如果想“勾住”在useEffect中创建的变量,那是做不到的。
ref属性有3种用法
字符串的形式
class Child extends React.Component {
constructor() {
super();
this.state = {
books: "三国演义",
age: "20"
};
}
showDivRef = () => {
// 只有字符串的形式是通过refs来获取的
console.log(this.refs); // {book: HTMLDivElement, btn: HTMLButtonElement}
};
render() {
return (
<div>
<div ref="book" id="book">
Jack给的书:{this.state.books}
</div>
<button onClick={this.showDivRef} ref="btn">
展示Refbook
</button>
{/* <FnChild ref="fn"></FnChild> */}
</div>
);
}
}
⚠️字符串的形式,控制台会给出警告让你使用 useRef()或者createRef()
ref属性是一个函数
当真实DOM创建完成之后,通过callback的形式,回调函数的第一个参数即是真实DOM或组件实例。在函数组件中使用的话,就给出警告让你使用useRef
class Child extends React.Component {
constructor() {
super();
this.state = {
books: "三国演义",
age: "20"
};
this.ref1 = null;
this.ref2 = null;
}
showDivRef = () => {
console.log(this.refs); // {}
console.log(this.ref1); // <div id="book">Jack给的书:三国演义</div>
console.log(this.ref2); // <button>展示Refbook</button>
};
render() {
return (
<div>
<div ref={(arg) => (this.ref1 = arg)} id="book">
Jack给的书:{this.state.books}
</div>
<button onClick={this.showDivRef} ref={(arg) => (this.ref2 = arg)}>
展示Refbook
</button>
</div>
);
}
}
ref属性是一个对象
上面已经介绍过了
Ref对象其实就是一个带有current属性的对象。
export function createRef() {
const refObject = { current: null, }
return refObject;
}
ref的高阶用法
forwardRef
顾名思义,就是转发Ref, 可跨组件获取ref对象(孙组件获取到爷组件的ref对象,即可以在爷组件中可以获取到子组件的DOM或组件等)
export default function App() {
const [currentRef, setCurrentRef] = useState(null);
const getRef = () => {
console.log(currentRef); // <span>我是子组件</span>
};
return (
<div className="App">
<button onClick={getRef}>获取子组件的ref</button>
<NewParent ref={(node) => setCurrentRef(node)}></NewParent>
</div>
);
}
// 获取到在爷组件定义的ref对象,就可以将子组件某元素或组件放入爷组件的ref对象中
const NewParent = React.forwardRef((props, ref) => (
<Parent AppRef={ref} {...props} />
));
function Parent(props) {
return (
<div>
父组件
<Son AppRef={props.AppRef}></Son>
</div>
);
}
function Son(props) {
const { AppRef } = props;
return (
<div>
<span ref={AppRef}>我是子组件</span>
{/* <Son2 ref={AppRef}></Son2> */}
</div>
);
}
Context
父子组件可以通过props传递数据,但是如果是多层级的话,会很不方便。这个时候就应该考虑使用context了。 实现跨层级传递数据,比如本地语言环境等
Provider提供context,provider中value属性改变会使所有context中的consumer组件更新。
使用方式:
React.createContext()
const Context = React.createContext(null)
export default function App() {
return (
<Context.Provider value={{ locale: "de" }}>
<Son></Son>
</Context.Provider>
);
}
function Son(props) {
return (
<Context.Consumer>
{(val) => {
return <span>{val.locale}</span>; // de
}}
</Context.Consumer>
);
}
Privider逐层传递
Provider可以逐层传递context,下一层级的Provider会覆盖上一层级的Provider。React-redux中connect就是利用这个特性传递Provider的。
const Context = React.createContext(null);
function Son2() {
return (
<Context.Consumer>
{(context) => {
return <div className="sonbox"> 第二层role:{context.role}</div>;
}}
</Context.Consumer>
);
}
function Son() {
const { role } = React.useContext(Context);
const [context] = React.useState({ role: "user" });
/* 第二层 Provder 传递内容 */
return (
<div className="box">
第一层role:{role}
<Context.Provider value={context}>
<Son2 />
</Context.Provider>
</div>
);
}
export default function Demo() {
const [themeContextValue] = React.useState({ role: "admin" });
/* 第一层 Provider 传递内容 */
return (
<Context.Provider value={themeContextValue}>
<Son />
</Context.Provider>
);
}