The introduction

This article explores a solution implemented using React hooks from the most basic implementation

Xiao Ming encountered this problem when writing code


Problems encountered

Xiaoming has a page containing these two functions, respectively upload files and upload pictures, the two in addition to different styles, upload logic is basically the same, how to elegant and reasonable implementation? (The red and blue components in the figure)



Analyze the two component components



A) Upload file components

  • The upload button
  • The upload path displays a text area

B) Upload picture component

  • The upload button
  • Upload description document

plan

Solution 1: The original solution — coupled together

Xiaoming: Both components have button uploads. Try putting them together, so that the logic can be shared

import { Upload } from 'antd';

class Upload extends Component {
  state = {
    fileList: [].uploadUrl: ' '}; render() {const { uploadType, btnText, btnDes, ... } = this.props;
    const { fileList, uploadUrl } = this.state;

    const uploadProps = {
      name: 'file'.action: '/api/uploadFile'.fileList: fileList,
      onChange: info= > {
        const { file } = info;
        // Determine the current state
        if (file.status === 'done') {
          const { response: { url } } = file;
          this.setState({
            uploadUrl: url,
          });
        } else if (file.status === 'error') {
          // do something
        }
        // Update file list display: Only one uploaded file is displayed
        this.setState({
          fileList: info.fileList.slice(- 1)}); }};// Render component styles
    const renderElement = (a)= > {
      switch (uploadType) {
        caseUpload pictures:return <UploadImage/>
        caseUploading files:return <UploadFile/>}}return (
      <div className={styles.container}>
        <Upload {. uploadProps} >
          {renderElement()}
        </Upload>
      </div>); }}Copy the code

The two components are coupled together and need to be passed in to uploadType to distinguish between the styles currently displayed

The original progression of scenario 2 – split into two components

Xiaoming: Actually said my code is not good, that I split into two components!

import { Upload } from 'antd';

class UploadImage extends Component { state = { ... As above}; render() {const { fileList, uploadUrl, btnText, btnDes } = this.state;

    const uploadProps = {
      name: 'file'.action: '/api/uploadFile'.fileList: fileList,
      onChange: info= >{... Upload processing logic, same as above... }};return (
      <div className={styles.container}>
        <Upload {. uploadProps} >. Upload picture styles...</Upload>
      </div>); }}// Upload the file UploadFile as defined above, but

class UploadFile extends Component {... return (<div className={styles.container}>
        <Upload {. uploadProps} >. Upload file styles...</Upload>
      </div>); }}Copy the code



The little green Review

Option 3 from primitive to advanced – render child mode

UploadProps = uploadProps = uploadProps

import { Upload } from 'antd';

class UploadRender extends Component { state = { ... As above}; render() {const { fileList, uploadUrl } = this.state;

    const uploadProps = {
      name: 'file'.action: '/api/uploadFile'.fileList: fileList,
      onChange: info= >{... Handle upload logic, ibid... }};return (
      <div className={styles.container}>
        <Upload {. uploadProps} >This.props. Children {this.props. Children}</Upload>
      </div>); }}export default UploadRender
Copy the code

use

<UploadRender> <UploadImage/> </UploadRender>Copy the code

Fatal flaw: The UploadImage child cannot get attributes inside the package directly.

Xiaoming: Pawn…

Scheme 4 Advanced scheme – higher order components

Xiaoming continues to improve, try higher-order components… Step1. Define a higher-order component

const UploadWrapper = WrapperedComponent= > class WrapperComponent extends Component { state = { ... As above}; render() {const { fileList, uploadUrl } = this.state;

      const uploadProps = {
        name: 'file'.action: '/api/uploadFile'.fileList: fileList,
        onChange: info= >{... Handle upload logic, ibid... }};/ / key point
      return (
        <WrapperedComponent uploadProps={props} {. this.props} / >
      );
    }
  }

export default UploadWrapper;
Copy the code

Step2. Define the style

import { Upload } from 'antd';

// Define the upload image style
const UploadImage = ({ uploadProps, btnText, btnDes }) = > {
  return <Upload {. uploadProps} >
    <div className={styles.container}>. Upload picture styles...</div>
  </Upload>
};

// Define the upload file style
const UploadFile = ({ uploadProps, btnText, btnDes }) = > {
  return <Upload {. uploadProps} >
    <div className={styles.container}>. Upload file styles...</div>
  </Upload>
};
Copy the code

3. Use posture

// Use the high order component UploadWrapper wrapper
const UploadImageWrapper = UploadWrapper(UploadImage);
constUploadFileWrapper = UploadWrapper(UploadFile); <UploadImageWrapper btnText={... } {... Other attributes}/> <UploadFileWrapper btnText={... } {... Other attributes}/>Copy the code

Xiaoming inner OS: Finally the logic is out perfectly ** Xiaoling: ** Using multi-layer nesting, seems a little tedious? How about a different position?

Scheme 5 Advanced scheme advanced -Render Props

Xiao Ming continued to move the bricks…

import { Upload } from 'antd';

class UploadRender extends Component { state = { ... As above}; render() {// Pass the rendering style externally
    const {render} = this.props;
    const { fileList, uploadUrl } = this.state;

    const props = {
      name: 'file'.action: '/api/uploadFile'.fileList: fileList,
      onChange: info= >{... Handle upload logic, ibid... }};return (
      <div className={styles.container}>
        <Upload {. props} >{render(fileList, uploadUrl)}</Upload>
      </div>); }}Copy the code

Use the pose

// Upload the image
<UploadRender render={(fileList, uploadUrl) => {
    	return<UploadImage fileList={fileList} uploadUrl={uploadUrl}/>}/> // UploadRender render={(fileList, uploadUrl) => { return <UploadImage fileList={fileList} uploadUrl={uploadUrl}/> }/>Copy the code

Small green continue to comment: seems to be better than the higher-order components? But it feels a bit weird (rendering with attribute inputs is a bit anti-human to read the code)

React Hooks

Step1. Custom hooks

import { useState } from '@alipay/bigfish/react';

export function useUpload() {
  const [uploadUrl, setUploadUrl] = useState(' ');
  const [fileList, setFileList] = useState([]);
  const uploadProps = {
    name: 'file'.action: '/api/uploadFile'.fileList: fileList,
    onChange(info) {
      const { file } = info;
      // Determine the current state
      if (file.status === 'done') {
        const { response: { url } } = file;
        setUploadUrl(url);
      } else if (file.status === 'error') {
        // do something
      }
      // Only one uploaded file is displayed
      setFileList(info.fileList.slice(- 1)); }};return {
    uploadProps,
    uploadUrl,
    fileList,
  };
}

Copy the code

Step2. Use posture

import { useUpload } from '.. /.. /Hooks/upload'; // Const UploadImage = ({btnText = 'UploadImage ', btnDes =' preview '}) => {const {uploadProps, uploadUrl } = useUpload(); return ( <div className={styles.container}> <Upload {... uploadProps}> ... Upload picture styles... </div> </Upload> </div> ); }; // Const UploadImage = ({btnText = 'uploadProps'}) => {const {uploadProps, uploadUrl} = useUpload(); return ( <div className={styles.container}> <Upload {... uploadProps}> ... Upload file styles... </div> </Upload> </div> ); };Copy the code

Xiaoming: I’m good, aren’t I?

Scheme comparison

plan review Individual be fond of
Solution 1: The original solution — coupled together If each style is different, it is not convenient to maintain
The original progression of scenario 2 – split into two components There is the problem of logic duplication
Option 3 from primitive to advanced – render child mode Parent and child components do not communicate directly and are better suited to the view layer Personally, I like it.
Scheme 4 Advanced scheme – higher order components Nesting, with layers of higher-order components wrapped around it, may not be good Personally, I like it.
Scheme 5 Advanced scheme advanced -Render Props Reading complex code is not intuitive
React Hooks Functional code is simpler, more reusable, offers the possibility of easy testing, and version requirements are high Personal recommendations

The final summary

Specific choice plan wants to see development time to decide of course