数据传递和方法调用
组件之间经常需要互相进行传递数据和方法调用,这种情况大部分都出现在父子组件中,一般会用到这样几种传递方式:
- 父组件传递数据到子组件
- 子组件传递数据到父组件
- 子组件调用父组件的方法
- 父组件调用子组件的方法
父组件传递数据到子组件比较简单,我们直接使用props
传递数据即可;而子组件调用父组件的方法其实也不复杂,我们通过props
将父组件中的回调函数传递给子组件,就间接实现子组件调用父组件的方法;子组件向父组件传递数据,这通过子组件调用父组件的方法就可以实现。至于父组件调用子组件的场景在实际开发中相对少见,一般开发中都是通过props
传递一些状态变量来驱动子组件变化的。
父组件传递数据到子组件
父组件传递数据到子组件非常简单,我们直接用props
属性传递数据即可。
ParentComponent.jsx
import ChildComponent from "./ChildComponent";
const ParentComponent = () => {
return <ChildComponent text="hello"></ChildComponent>;
};
export default ParentComponent;
ChildComponent.jsx
const ChildComponent = (props) => {
return <div>{props.text}</div>;
};
export default ChildComponent;
子组件调用父组件的方法
子组件调用父组件的方法和子组件向父组件传递数据是等同的,父组件通过props
将回调函数传递给子组件即可,通过回调函数参数即可实现数据传递,下面是一个例子。
ParentComponent.jsx
import ChildComponent from "./ChildComponent";
const ParentComponent = () => {
const callbackFunc = (data) => {
console.log(data);
};
return <ChildComponent callbackFunc={callbackFunc}></ChildComponent>;
};
export default ParentComponent;
ChildComponent.jsx
const ChildComponent = (props) => {
return (
<div>
<button
onClick={() => {
props.callbackFunc("hello");
}}
></button>
</div>
);
};
export default ChildComponent;
代码中父组件在子组件的HTML标签上添加了callbackFunc
属性,子组件中使用props.callbackFunc
接受该函数并调用,此时便可实现调用父组件函数并传递数据了。
父组件调用子组件方法
父组件调用子组件需要用到useRef()
获取子组件的引用。
ParentComponent.jsx
import { useRef } from "react";
import ChildComponent from "./ChildComponent";
const ParentComponent = () => {
const childRef = useRef();
return (
<>
<ChildComponent ref={childRef} />
<button
onClick={() => {
childRef.current.func();
}}
>
Click Me
</button>
</>
);
};
export default ParentComponent;
子组件中用到了React.forwardRef()
,该API用于在函数式组件中支持ref
的传递;此外我们还用到了useImperativeHandle
,它用于在函数式组件中暴露特定的值或方法给父组件。
ChildComponent.jsx
import { forwardRef, useImperativeHandle } from "react";
const ChildComponent = forwardRef((props, ref) => {
const func = () => {
console.log("func called");
};
useImperativeHandle(ref, () => ({
func,
}));
return <div></div>;
});
export default ChildComponent;
不具有父子关系的组件之间传递数据
React提供了ContextAPI能够实现全局的状态数据,通过全局数据就能够实现任意两个或多个组件之间的数据传递了。在React早期版本中使用Context.Provider
和Context.Consumer
来设置和读取全局数据,新版本提供了useContext()
这个Hooks函数,使用更加方便。
ComponentA.jsx
import React, { useState } from "react";
import ComponentB from "./ComponentB";
export const UserContext = React.createContext({});
const ComponentA = () => {
const [user, setUser] = useState({
username: "tom",
password: "abc123",
});
return (
<UserContext.Provider
value={{
user,
setUser,
}}
>
<ComponentB></ComponentB>
</UserContext.Provider>
);
};
export default ComponentA;
ComponentB.jsx
import ComponentC from "./ComponentC";
const ComponentB = () => {
return (
<>
<ComponentC></ComponentC>
</>
);
};
export default ComponentB;
ComponentC.jsx
import { useContext } from "react";
import { UserContext } from "./ComponentA";
const ComponentC = () => {
const { user } = useContext(UserContext);
return <div>{JSON.stringify(user)}</div>;
};
export default ComponentC;
上面组件代码的嵌套关系如下:
在组件A中我们使用了React.createContext()
创建了名为UserContext
的对象,<UserContext.Provider>
包裹的组件都可以获取它的值。注意这里我们实际上是将组件A的状态数据user
和setUser
都传递给了Context,实际上Context可以传递任意数据,代码中的写法是为了子组件可以调用user
获取数据,调用setUser()
修改数据;如果子组件只需要读取数据,那么不传setUser
也是完全可以的。
在组件C中我们使用useContext()
获取了Context中的数据,并将其JSON形式显示在了页面中。
使用全局的状态参数要小心,如果设计的不合理,很可能使得应用的数据流变得混乱。对于数据流很复杂的应用,应该考虑使用Redux,有关Redux的内容将在后续章节介绍。