This is the 7th day of my participation in Gwen Challenge

Click no effect, always feel less something

Antd has its own menu navigation, there is no special cool effect when clicking menu items, so we will add it by ourselves.

Drew lessons from the realization of a big guy on the net, and on its basis to complete the perfect

Big guy’s implementation

On its basis, I complete the function, so that it can solve the problems I encountered:

First of all, the menu item is multiple and single click switch, the big man implementation is not very good to complete this point, when there are multiple nodes with this effect, once refreshed, all nodes will trigger the refresh, that is, the full screen ripple, this point must be solved.


Start refactoring to meet expectations

Start by making a higher-order component so that you can use TA to wrap other components and make them clickable and easy to use.

The component named withMaterialHoc, first post the implementation of the component:

withMaterialHoc/index.js

import React from 'react';
import { Ripple, calcEventRelativePos } from './ripple';

let withMaterialHoc = (WrappedComponent) = > {
  return class WithMaterial extends React.Component {
    constructor(props) {
      super(props)
      this.isSwitchIntl = false;
      this.state = {
        spawnData: "".clickCount: 0.isSwitchIntl: false
      }
    }

    handleClick = (event) = > {
      this.setState({
        spawnData: calcEventRelativePos(event),
        time: Date.now(),
        clickCount: this.state.clickCount + 1
      });
    }

    shouldComponentUpdate(nextProps, nextState) {
      if(nextProps.intlData ! =this.props.intlData) {
        return true;
      }
      if (nextState.clickCount && (nextState.clickCount - this.state.clickCount === 1)) {
        return true
      } else {
        return false
      }
    }

    handleRippleEnd = () = > {
      let value = this.state.clickCount - 1
      if (value < 0) {
        value = 0
      }
      this.setState({
        clickCount: value
      })
    }

    render() {
      const { spawnData } = this.state;
      const { className, style } = this.props;
      return (
        <div
          className={`g-btnThe ${className` | | '}}onClick={this.handleClick}
          style={style}
        >
          <WrappedComponent {. this.props} / >
          <Ripple handleRippleEnd={this.handleRippleEnd} spawnData={spawnData} />
        </div>); }}; }export default withMaterialHoc;
Copy the code

Ripple. Js:

import React, { useState, useEffect, useRef, Fragment,memo } from 'react';
import './index.css';
import { useSpring, animated } from 'react-spring';

function calcEventRelativePos(event) {
  const rect = event.target.getBoundingClientRect();
  return {
    x: event.clientX - rect.left,
    y: event.clientY - rect.top,
  };
}
function Ripple(props) {
  const [data, setData] = useState({ top: 0.left: 0.width: 0.height: 0 });
  const isInit = useRef(true);
  const rippleEl = useRef(null);
  const { spawnData, handleRippleEnd} = props;
  const rippleAnim = useSpring({
    from: {... props.style, ... data,transform: 'scale(0)'.opacity: 1,},to: !isInit.current ? { opacity: 0.transform: 'scale(2)'}, {},config: {
      duration: props.duration || 300,},onRest: () = > {
      handleRippleEnd()
    },
    reset: true
  });

  useEffect(() = > {
    if (isInit.current) {
      isInit.current = false;
    } else {
      const parentEl = rippleEl.current.parentElement;
      const size = Math.max(parentEl.offsetWidth, parentEl.offsetHeight);
      setData({
        width: size,
        height: size,
        top: spawnData.y - size / 2 || 0.left: spawnData.x - size / 2+50 || 50
      });
    }
  }, [spawnData]);
  return (
    <animated.span
          className="g-ripple"
          style={rippleAnim}
          ref={rippleEl}
        ></animated.span>
  );
}

Ripple = memo(Ripple)

export { Ripple, calcEventRelativePos };

Copy the code

Analysis:

Why not the big guy implementation?

Since Ripple is a functional component, each time the parent component refreshes, the child component refreshes, thus starting the animation (implemented using the React-Spring library).

So the key to solving this problem is, which node I click on, which node triggers the refresh, and the other nodes do not refresh.

Use memo to control the refresh of function components

React.memo

React.memo is a higher-order component. It is very similar to the React.PureComponent, but it applies to function components, not class components.

If the parent component passes the props to the child component unchanged or the referenced address does not change, then the function component wrapped by memo will not trigger the refresh.

Take a look at the effect

conclusion

The effect is rather short of superfluous, sometimes just the right ornament, will play an unexpected effect, inside and outside concurrently repair, is the best realization, hurriedly add try.

🌰 integrates this functionality