1
头图

The ability to write components is an important indicator to measure the level of front-end engineers, whether it is a basic component or a business component.
The author also likes to write components in my spare time. In order to help beginners to write React components, and to share my experience and ideas in writing components, I decided to open a series, namely: 动手撸组件系列 , to share some with you Implementation methods and techniques of public components and business components.

As the first article in this series, share how to implement a Collapse component from zero to one

Collapse basic UI drawing

As a basic component, the accordion panel consists of two parts: the first part is the title area, and the second part is the collapsible area. Click the title area to collapse and expand the content area. For the aesthetics of the component you can add an arrow icon to the right of the header to rotate it when expanding and collapsing.

In order to reduce the cost of environment construction, the practice uses the create-react-app environment, and creates the node create-react-app development environment.

 npx create-react-app 项目名称

It should be noted that the project name must be in English, create-react-app will automatically create a directory for us.

After the project is created, create a file named Collapse.jsx in the src directory, and enter the following code:
(Beginners can choose to copy)

 import React, { useState } from "react";
import "./style.css";

const CollapsablePanel = () => {
    const [isCollapsed, setIsCollapsed] = useState(true);

    const togglePanel = () => {
        setIsCollapsed((prevState) => !prevState);
    };
    return (
        <div className="wrapper">
            <div className="pannel" onClick={togglePanel}>
                <div className="heading">
                    <span>Flower Collapse</span>
                    <svg width="20px"
                        height="25px" viewBox="0 0 1024 1024"
                        style={{ color: '#6495ed' }}><path d="M64 351c0-8 3-16 9-22.2 12.3-12.7 32.6-13.1
                         45.3-0.8l394.1 380.5L905.7 328c12.7-12.3 33-12 45.3 0.7s12 33-0.7 45.3L534.7 
                         776c-12.4 12-32.1 12-44.5 0L73.8 374c-6.5-6.3-9.8-14.6-9.8-23z" p-id="1705">
                        </path></svg>
                </div>
                <div
                    className="content"
                >
                    <div className="contentInner" >
                        Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
                        nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam
                        erat, sed diam voluptua.
                    </div>
                </div>
            </div>
        </div>
    );
};

export default CollapsablePanel;

Then create a style file named style.css

 .wrapper {
    display: flex;
    justify-content: center;
    width: 100%;
    height: 100vh;
    background-color: rgb(228, 239, 239);
    padding-top: 40vh;
}

.pannel {
    width: 400px;
    text-align: left;
}

.heading {
    background-color: #bfa;
    border-top-left-radius: 10px;
    border-top-right-radius: 10px;
    color: #000;
    font-size: 20px;
    line-height: 20px;
    border: 1px solid rgb(212, 240, 205);
    padding: 10px 20px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    cursor: pointer;
}

.content {
    font-size: 20px;
    background: #fff;
    border: 1px solid #fff;
    border-top: none;
    padding: 0 20px;
    color: #000000;
    overflow: hidden;
}

.contentInner {
    padding: 20px 0;
}

After creating the above two files, mount the created Collapse component in index.js :
(The original code is irrelevant, just delete it directly)

 import React from 'react';
import ReactDOM from 'react-dom/client';
import Collapse from './components/Collapse';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Collapse />
);

After the creation is complete, use the yarn start command to start the application, and you can see the appearance of the drawn Collapse component:

Collapse组件UI图

After completing the basic UI drawing of Collapse, review what operations have been done:
First define a state named isCollapsed , store the component expansion and closing state, and declare a method named togglePanel , which can be called when the user clicks on the title to expand the panel closure.
Then define the div container with style names of pannel, heading, content and its related sub-containers, and set the style of the container in style.css . The ICON in the component is drawn directly using the svg tag to avoid increasing the size of the component due to the introduction of the svg package.

Content area expansion animation

There are many ways to implement animation, you can use the transition property of CSS, or you can use the various animation libraries of the React ecosystem. In the React ecosystem, there is a very popular animation library called react-spring , which is not only powerful, but also supports hook method calls. This article uses this animation library to realize content area expansion animation and button rotation animation.

Install react-spring animation library

 yarn add react-spring

After installing the react-spring animation library, you can define a method to let spring help us generate animation styles

 const panelContentAnimatedStyle = useSpring({
    height: isCollapsed ? 0 : 200,
  });

Then change the tag name of the content area from <div> to <animated.div> (same as useSpring, animated is also an object of react-spring), and add the just created panelContentAnimatedStyle :

 import React, { useState } from "react";
import { useSpring, animated } from "react-spring";
import "./style.css";

const CollapsablePanel = () => {
    const [isCollapsed, setIsCollapsed] = useState(true);

    const togglePanel = () => {
        setIsCollapsed((prevState) => !prevState);
    };
    const panelContentAnimatedStyle = useSpring({
        height: isCollapsed ? 0 : 180,
    });
    return (
        <div className="wrapper">
            <div className="pannel" onClick={togglePanel}>
                <div className="heading">
                    <span>Flower Collapse</span>
                    <svg width="20px"
                        height="25px" viewBox="0 0 1024 1024"
                        style={{ color: '#6495ed' }}><path d="M64 351c0-8 3-16 9-22.2 12.3-12.7 32.6-13.1
                         45.3-0.8l394.1 380.5L905.7 328c12.7-12.3 33-12 45.3 0.7s12 33-0.7 45.3L534.7 
                         776c-12.4 12-32.1 12-44.5 0L73.8 374c-6.5-6.3-9.8-14.6-9.8-23z" p-id="1705">
                        </path></svg>
                </div>
                <animated.div
                    style={panelContentAnimatedStyle}
                    className="content"
                >
                    <div className="contentInner" >
                        Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
                        nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam
                        erat, sed diam voluptua.
                    </div>
                </animated.div>
            </div>
        </div>
    );
};

export default CollapsablePanel;

Click the title bar to see the following effects:

collapse.gif

Wait~~ Is the height fixed?

Obviously not! When the user uses the Collapse component, the content passed is not only text, but also images or any type of ReactNode, so when expanding, it is necessary to obtain the actual height of the content object. There is a library for obtaining the height of the DOM object. Help us, react-use-measure , this library can measure not only the length and width of DOM objects, but also the position of DOM objects from the top, bottom, left and right of the browser.
react-use-measure provides a hook named useMeasure , which is used as follows:

 const [ref, bounds] = useMeasure();

The first parameter is the ref object, which can be bound to the ref attribute of the DOM object to be measured. The second bounds is the position object, which contains all the attributes mentioned above.

Continue to modify the component code Collapse.jsx :

 import React, { useState } from "react";
import { useSpring, animated } from "react-spring";
import useMeasure from 'react-use-measure'
import "./style.css";

const CollapsablePanel = () => {
    const [isCollapsed, setIsCollapsed] = useState(true);
    const [ref, bounds] = useMeasure();

    const togglePanel = () => {
        setIsCollapsed((prevState) => !prevState);
    };
    const panelContentAnimatedStyle = useSpring({
        height: isCollapsed ? 0 : bounds.height,
    });
    return (
        <div className="wrapper">
            <div className="pannel" onClick={togglePanel}>
                <div className="heading">
                    <span>Flower Collapse</span>
                    <svg width="20px"
                        height="25px" viewBox="0 0 1024 1024"
                        style={{ color: '#6495ed' }}><path d="M64 351c0-8 3-16 9-22.2 12.3-12.7 32.6-13.1
                         45.3-0.8l394.1 380.5L905.7 328c12.7-12.3 33-12 45.3 0.7s12 33-0.7 45.3L534.7 
                         776c-12.4 12-32.1 12-44.5 0L73.8 374c-6.5-6.3-9.8-14.6-9.8-23z" p-id="1705">
                        </path></svg>
                </div>
                <animated.div

                    style={panelContentAnimatedStyle}
                    className="content"
                >
                    <div ref={ref} className="contentInner" >
                        Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
                        nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam
                        erat, sed diam voluptua.
                        Lorem ipsum, dolor sit amet consectetur adipisicing elit.
                         Quasi aperiam dignissimos eaque deserunt expedita sit 
                         accusamus sunt laudantium repellendus nisi! Sit, 
                         consequuntur. Tempora, officiis molestiae 
                         fuga sit quae aliquid maxime.
                    </div>
                </animated.div>
            </div>
        </div>
    );
};

export default CollapsablePanel;

Current effect:

collapse2.gif
Using react-use-measure can be very convenient to obtain the real height and position of the DOM object in the browser, and flexible use in the project can improve the development efficiency.

Implement arrow icon rotation animation

The rotation of the arrow icon is similar to the implementation of the content area, just change its label to animated.div and bind the style object generated by useSpring.
Generate arrow ICON rotation animation style object:

 const toggleWrapperAnimatedStyle = useSpring({
    transform: isCollapsed ? "rotate(0deg)" : "rotate(180deg)",
  });

The svg object wraps a div and binds the animation style:

 import React, { useState } from "react";
import { useSpring, animated } from "react-spring";
import useMeasure from 'react-use-measure'
import "./style.css";

const CollapsablePanel = () => {
    const [isCollapsed, setIsCollapsed] = useState(true);
    const [ref, bounds] = useMeasure();

    const togglePanel = () => {
        setIsCollapsed((prevState) => !prevState);
    };
    const panelContentAnimatedStyle = useSpring({
        height: isCollapsed ? 0 : bounds.height,
    });
    const toggleWrapperAnimatedStyle = useSpring({
        transform: isCollapsed ? "rotate(0deg)" : "rotate(180deg)",
    });
    return (
        <div className="wrapper">
            <div className="pannel" onClick={togglePanel}>
                <div className="heading">
                    <span>Flower Collapse</span>
                    <animated.div style={toggleWrapperAnimatedStyle}>
                        <svg width="20px"
                            height="25px" viewBox="0 0 1024 1024"
                            style={{ color: '#6495ed' }}><path d="M64 351c0-8 3-16 9-22.2 12.3-12.7 32.6-13.1
                         45.3-0.8l394.1 380.5L905.7 328c12.7-12.3 33-12 45.3 0.7s12 33-0.7 45.3L534.7 
                         776c-12.4 12-32.1 12-44.5 0L73.8 374c-6.5-6.3-9.8-14.6-9.8-23z" p-id="1705">
                            </path></svg>
                    </animated.div>
                </div>
                <animated.div

                    style={panelContentAnimatedStyle}
                    className="content"
                >
                    <div ref={ref} className="contentInner" >
                        Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
                        nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam
                        erat, sed diam voluptua.
                    </div>
                </animated.div>
            </div>
        </div>
    );
};

export default CollapsablePanel;

You can see the following effects:

collapse3.gif

Summarize

动手撸组件系列 The first article chooses to talk about the implementation of the Collapse component, because the implementation of this component is simple and interesting, and you can experience the fun of writing components while reading. A simple component may also encounter problems when implementing, such as how to obtain the height in the content area, which is very representative. The current ecosystems of React and Vue are extremely prosperous. Developers should be able to flexibly use these open source libraries to develop concise and powerful components when implementing specific needs.

Coollapse.jsx source code
style.css source code


不羁的风
35 声望4 粉丝

天下事有难易乎? 为者,则难者亦易已;不为,则易者亦难矣!