lyzMaster
lyzMaster

Reputation: 650

Rewrite a react class component as a functional component

I try to use a library Tick, but it only has a demo, which is written in React class component.

import React from "react";
import Tick from "@pqina/flip";
import "@pqina/flip/dist/flip.min.css";

export default class Flip extends React.Component {
  constructor(props) {
    super(props);
    this._tickRef = React.createRef();
  }

  componentDidMount() {
    this._tickInstance = Tick.DOM.create(this._tickRef.current, {
      value: this.props.value
    });
  }

  componentDidUpdate() {
    if (!this._tickInstance) return;
    this._tickInstance.value = this.props.value;
  }

  componentWillUnmount() {
    if (!this._tickInstance) return;
    Tick.DOM.destroy(this._tickRef.current);
  }

  render() {
    return (
      <div ref={this._tickRef} className="tick">
        <div data-repeat="true" aria-hidden="true">
          <span data-view="flip">Tick</span>
        </div>
      </div>
    );
  }
}

I'd like to use the library as a Hook component, the following code is what I rewritten:

import Tick from "@pqina/flip";
import "@pqina/flip/dist/flip.min.css";
import React, { useEffect, useRef, useState } from 'react';

interface IFilpProps {
    value: number
}

const Filp: React.FC<IFilpProps> = (props) => {

    const ref = useRef<HTMLDivElement>(null);
    const [tickInstance, setTickInstance] = useState<any>();

    useEffect(() => {
        setTickInstance(Tick.DOM.create(ref.current, {
            value: props.value
        }));
        return () => {
            Tick.DOM.destroy(ref.current);
        }
    }, []);

    useEffect(() => {
        tickInstance?.value && (tickInstance.value = props.value);
    }, [props.value])

    return <div ref={ref} className="tick">
        <div data-repeat="true" aria-hidden="true">
            <span data-view="flip">Tick</span>
        </div>
    </div>
}

outside the component, I have a button, every time I click it adds one:

const [flipSecond, setFlipSecond] = useState<number>(0);
// Omit some not important code
<Flip value={flipSecond}></Flip>
<button onClick={() => setFlipSecond(flipSecond+1)}>+1</button>

The demo which uses class component works fine, but my hook code can't work normally: when I click the '+1' button, the Flip component won't plus 1. Could you help me find out the the reason?

Upvotes: 0

Views: 299

Answers (2)

mcAdam
mcAdam

Reputation: 23

This is a rewrite for the historic counter

Eg. Counts the time passed since the start of the millenium in years, months, days, hours, minutes and seconds.

The main thing to take away from here is the use of useRef() and how we are using it when creating the tickInstance with Tick.DOM.create()

const FlipClock = () => {

  const tickRef = useRef();

  useEffect(() => {
   const tickInstance = Tick.DOM.create(tickRef.current);

   Tick.count.up("2000-01-01T00:00:00", 
   {format: ["y", "M", "d", "h", "m", "s"],})
   .onupdate = (value) => {
  tickInstance.value = value;
 };
});

return (
    <div ref={tickRef} style={{ fontSize: "3rem" }}>
      <div data-repeat="true" aria-hidden="true">
        <span data-view="flip">Tick</span>
      </div>
   </div>
 );
};

export default FlipClock;

Demo

Upvotes: 0

kboul
kboul

Reputation: 14570

You should use a ref instead of state to store tickInstance. In general if you want to replace variables that used to be stored in the constructor before, it is recommended now to be stored as refs when rewriting class based components to functional.

export default function Flip({value}) {
  const tickRef = React.useRef();
  let tickInstance = React.useRef()


  useEffect(()=>{
    tickInstance.current = Tick.DOM.create(tickRef.current, {
      value
    });

    return () =>  Tick.DOM.destroy(tickRef.current);
  },[])

  useEffect(()=>{
    if (!tickInstance.current) return;
    tickInstance.current.value = value;
  },[value])

  return (
    <div ref={tickRef} className="tick">
    <div data-repeat="true" aria-hidden="true">
      <span data-view="flip">Tick</span>
    </div>
  </div>
  )
}

Demo

Upvotes: 2

Related Questions