如果觉得还有点用,请您给我一个赞!您的赞是我坚持下去的动力
环境:react 16.12.0
这里将会用类和函数组件2种方法实现同一目标来对比他们的区别
目录
- 子组件向父组件通信的方法(如何调用父组件方法)
- 父组件向子组件通信的方法(如何调用子组件方法)
- setState对象成员合并问题
- state变化
子组件向父组件通信,调用父组件方法
案例:
我们实现一个子组件Input,带有一个输入框和一个提交按钮
再实现一个父组件Form,希望当Input组件内用户输入完毕点提交后,父组件能获得到通知并拿到Input的值
- 函数式组件实现方式
函数式子组件 Input.js
//写一个Input组件,作为子组件用
function Input(props){
//用于存放输入的值
const [state, setState] = useState({inputValue:''});
function onClick(){
//这里就是父组件传递给子组件的方法,子组件可以通过props直接调用
props.onSubmit(state.inputValue);
}
function onInput(v){
setState({inputValue:v.target.value});
}
return (
);
}
export default Input;
函数式父组件 Form.js
import React from 'react';
import Input from './Input.js';
function Form(props){
//提供给子组件使用
function onSubmit(value){
console.log(`子组件提交啦,用户输入的是:${value}`)
}
return (
);
}
- 类式组件实现方式
类式子组件 Input.js
class Input_Class extends React.Component{
constructor(props){
super(props);
this.state = {
inputValue:''
}
}
onClick=()=>{
this.props.onSubmit(this.state.inputValue);
}
onInput=(v)=>{
this.setState({inputValue:v.target.value});
}
render(){
return (
);
}
}
export default Input_Class;
类式父组件 Form.js
class Form_Class extends React.Component{
constructor(props){
super(props);
this.state = {}
}
onSubmit =(value)=>{
console.log(`子组件提交啦,用户输入的是:${value}`)
}
render(){
return (
);
}
}
父组件向子组件通信,调用子组件内的方法
案例:
我们实现一个Input组件作为子组件
实现一个Form组件作为父组件
父组件内通过按钮点击后需要调用子组件Input内的clear()方法来清空输入框
实现方法如下:
- 函数式组件实现方式
子组件 Input.js
import React, { useState,useImperativeHandle,forwardRef } from 'react';
//写一个Input组件,作为子组件用
function Input(props,ref){
//用于存放输入的值
const [state, setState] = useState({inputValue:''});
//用于清除输入框里的内容
function clear(){
setState({inputValue:''});
}
//这里就是用来暴露给ref.current下面的东西
useImperativeHandle(ref, () => ({
//我们暴露给ref.current.clear()这么一个方法,用来调用内部的clear()
clear: () => {
clear();//这里调用内部的clear,当然你可以直接将clear的实现体写在这里
}
}));
return (
);
}
Input= forwardRef(Input);
export default Input;
父组件 Form.js
import React, { useState,useRef } from 'react';
import Input from './Input.js';
function Form(props){
const refInput= useRef();//生成一个ref一会儿用来绑定Input子组件
function onClick(){
//通过ref.current可以获取子组件内暴露给父组件的所有对象
refInput.current.clear();
}
return (
);
}
- 类式组件实现方式
子组件 Input.js -- 如果子组件是类式组件,则不需要做任何特殊处理
class Input_Class extends React.Component{
constructor(props){
super(props);
this.state = {
inputValue:''
}
}
onClick=()=>{
this.props.onSubmit(this.state.inputValue);
}
clear=()=>{
this.setState({inputValue:''});
}
onInput=(v)=>{
this.setState({inputValue:v.target.value});
}
render(){
return (
);
}
}
父组件 Form.js
需要注意在antd2-3下,对于被Form.create()的组件,需要通过
来获取到真实ref
this.ref=ref} />
class Sortable_Class extends React.Component{
constructor(props){
super(props);
this.state = {}
this.refInput = React.createRef();
}
btnSubmit=(value)=>{
console.log(`input value:${value}`);
}
btnClearInput=()=>{
this.refInput.current.clear();
}
render(){
return (
);
}
}
setState
在函数式组件中,setState的时候不会合并对象成员,我们需要手动使用{...param,...newParam}等方式进行合并
函数式组件
function Counter() {
const [param,setParam] = useState({a:1,b:2})
useEffect(() => {
setParam({b:3});// 解决方案: setParam( {...param,...{b:3}} )
},[]);
return (
<>
param:{JSON.stringify(param)}
>
);
}
渲染输出
param:{b:3}
类式组件
class CounterClass extends React.Component{
constructor(props){
super(props);
this.state = {
a:1,
b:2,
}
}
componentDidMount() {
this.setState({b:3});
}
render(){
return (
<>
param:{JSON.stringify(this.state)}
>
);
}
}
渲染输出
param:{a:1,b:3}
但是如果遇到多层结构对象的时候,由于setState只做浅拷贝所以会出现问题
this.state = {
objList: {
objA: {
list: [],
selected: 0,
},
objB: {
list: [],
selected: 1,
},
}
}
var obj = {
objList:{
objB:{
list:[5,2],
selected:11
}
}
};
this.setState(obj);
这里setState后的结果是 {"objList":{"objB":{"list":[5,2],"selected":11}}} ,objA没了
解决方案是在多层的时候将外层的对象进行展开合并
var obj = {
objList:{
...this.state.objList, //展开父级
objB:{
list:[5,2],
selected:11
}
}
};
this.setState(obj);
state变化
当我们点击一个按钮时触发一个延时alert,时间到了打印count的值
函数式组件
function Counter() {
const [count,setCount] = useState(0);
//const countRef = useRef();
//useEffect(()=>{countRef.current=count});
function handleClick(count) {
setTimeout(() => {
alert('You clicked: ' + count);
//alert('You clicked:' + countRef.current);
}, 3000);
}
return (
{count}
);
}
加入点击Click Show Counter后点击3下Click me,则输出结果为: 0
这是由于handleClick调用时,count以当时的值存在闭包内,所以延时后打印的值任是0,解决方法为使用 useRef,代码片段中注释的部分打开
类式组件
export class CounterClass extends React.Component{
constructor(props){
super(props);
this.state = {count:0}
}
handleClick=()=>{
setTimeout(() => {
alert('You clicked: ' + this.state.count);
}, 3000);
}
render(){
return (
<>
{this.state.count}
>
);
}
加入点击Click Show Counter后点击3下Click me,则输出结果为: 3
如果觉得还有点用,请您给我一个赞!您的赞是我坚持下去的动力