简体   繁体   中英

Migrate from this.setState calls with callback to useState and useEffect

I have already used new react hooks for almost 4 months and it's a great adjustment for creating react components. Currently, I am trying to migrate from class components to functional components and have some issues with it. For example, let's discuss this case and compare function and class components to better understand the issue.

class UserInformation extends React.Component {
    state = {
        isBusy: false,
    };

    updateImage = (uploadedImageData: IUploadImage): void => {
        const imageData = {
            ImageData: base64ToArrayBuffer(uploadedImageData.ImageData.ImageData),
            FileName: uploadedImageData.FileName,
        };

        this.setState({ isBusy: true }, () => {
            this.props.updateSettingsUserImage(this.props.selectedUser, imageData)
                .then(() => this.setState({ isBusy: false }));
        });
    };

    deleteImage = (): void => {
        this.setState({ isBusy: true }, () => {
            this.props.deleteSettingsUserImage(this.props.selectedUser)
                .then(() => this.setState({ isBusy: false }));
        });
    };

    const { selectedUser } = this.props;

    return (
        <BaseInfoCardHeader
           withUploadableAvatar
           style={{ margin: '0 -12px' }}
           name={selectedUser.FullName}
           deleteImage={this.deleteImage}
           updateImage={this.updateImage}
           photopath={selectedUser.Photopath}                        
        />
    );
}

export default UserInformation;

And here is the functional component version of UserInformation

const UserInformation = (props) => { const [isBusy, setIsBusy] = useState(false);

    const updateImage = (uploadedImageData: IUploadImage): void => {
        const imageData = {
            ImageData: base64ToArrayBuffer(uploadedImageData.ImageData.ImageData),
            FileName: uploadedImageData.FileName,
        };
        setIsBusy(true);
    };

    const deleteImage = (): void => setIsBusy(true);

    useEffect(() => {
       if (isBusy) {
          updateSettingsUserImage(props.selectedUser, imageData)
             .then(() => setIsBusy(false));
       }
    }, [isBusy]);

   useEffect(() => {
       if (isBusy) {
          deleteSettingsUserImage(props.selectedUser)
             .then(() => setIsBusy(false));
       }
    }, [isBusy]);

    const { selectedUser } = this.props;

    return (
        <BaseInfoCardHeader
           withUploadableAvatar
           style={{ margin: '0 -12px' }}
           name={selectedUser.FullName}
           deleteImage={this.deleteImage}
           updateImage={this.updateImage}
           photopath={selectedUser.Photopath}                        
        />
    );
}

export default UserInformation;

We can see that inside a functional component, doesn't matter if triggered updateImage or deleteImage , in both cases 2 useEffects will run and make API calls. But in class component, API calls will be handled inside setState callback and only one API call will be requested. One option is to add another state for imageData and use it inside its useEffect dependencies list. But what if we don't have imageData and everything depends on isBusy . What is the best way to handle this kind of scenarios?

Why do you even need to set state in this component? Also be careful to not just convert class components to functional components for the novelty of it, class based components still have their place. But your example is in fact a good candidate.

    const UserInformation = ({props}) => {
        const [isBusy, setBusy] = useState(false)
        const updateImage = async (uploadedImageData: IUploadImage): void => {
            setBusy(true);
            const imageData = {
                ImageData: base64ToArrayBuffer(uploadedImageData.ImageData.ImageData),
                FileName: uploadedImageData.FileName,
            };
            await updateSettingsUserImage(props.selectedUser, imageData)
            setBusy(false);
        };

        const deleteImage = async (): void => {
          setBusy(true);
          await deleteSettingsUserImage(props.selectedUser);
          setBusy(false);
        }

        const { selectedUser } = props;

        return (
            <BaseInfoCardHeader
               withUploadableAvatar
               style={{ margin: '0 -12px' }}
               name={selectedUser.FullName}
               deleteImage={this.deleteImage}
               updateImage={this.updateImage}
               photopath={selectedUser.Photopath}                        
            />
        );
    }

    export default UserInformation;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM