Metoda cyklu życia ReactJS wewnątrz komponentu funkcji

135

Zamiast pisać moje komponenty wewnątrz klasy, chciałbym zamiast tego użyć składni funkcji.

Jak mogę nadpisać componentDidMount, componentWillMountwewnątrz elementów funkcyjnych?
Czy to w ogóle możliwe?

const grid = (props) => {
    console.log(props);
    let {skuRules} = props;

    const componentDidMount = () => {
        if(!props.fetched) {
            props.fetchRules();
        }
        console.log('mount it!');
    };
    return(
        <Content title="Promotions" breadcrumbs={breadcrumbs} fetched={skuRules.fetched}>
            <Box title="Sku Promotion">
                <ActionButtons buttons={actionButtons} />
                <SkuRuleGrid 
                    data={skuRules.payload}
                    fetch={props.fetchSkuRules}
                />
            </Box>      
        </Content>  
    )
}
Aftab Naveed
źródło
1
komponenty funkcjonalne nie powinny mieć metod cyklu życia. ponieważ są tylko funkcjami. a funkcje nie mają metod. są na to zajęcia
lawina 1

Odpowiedzi:

149

Edycja: Wraz z wprowadzeniem Hooksmożna zaimplementować rodzaj zachowania w cyklu życia, a także stan w komponentach funkcjonalnych. Obecnie

Hooki to nowa propozycja funkcji, która pozwala używać stanu i innych funkcji Reacta bez pisania klasy. Zostały wydane w React jako część wersji 16.8.0

useEffecthook może służyć do replikacji zachowania cyklu życia i useStatemoże służyć do przechowywania stanu w komponencie funkcji.

Podstawowa składnia:

useEffect(callbackFunction, [dependentProps]) => cleanupFunction

Możesz zaimplementować swój przypadek użycia w hookach, takich jak

const grid = (props) => {
    console.log(props);
    let {skuRules} = props;

    useEffect(() => {
        if(!props.fetched) {
            props.fetchRules();
        }
        console.log('mount it!');
    }, []); // passing an empty array as second argument triggers the callback in useEffect only after the initial render thus replicating `componentDidMount` lifecycle behaviour

    return(
        <Content title="Promotions" breadcrumbs={breadcrumbs} fetched={skuRules.fetched}>
            <Box title="Sku Promotion">
                <ActionButtons buttons={actionButtons} />
                <SkuRuleGrid 
                    data={skuRules.payload}
                    fetch={props.fetchSkuRules}
                />
            </Box>      
        </Content>  
    )
}

useEffectmoże również zwrócić funkcję, która zostanie uruchomiona, gdy komponent zostanie odmontowany. Można to wykorzystać do wypisania się z subskrypcji słuchaczy, replikując zachowanie componentWillUnmount:

Np .: componentWillUnmount

useEffect(() => {
    window.addEventListener('unhandledRejection', handler);
    return () => {
       window.removeEventListener('unhandledRejection', handler);
    }
}, [])

Aby useEffectuzależnić od określonych zdarzeń, możesz podać tablicę wartości do sprawdzenia zmian:

Np .: componentDidUpdate

componentDidUpdate(prevProps, prevState) {
     const { counter } = this.props;
     if (this.props.counter !== prevState.counter) {
      // some action here
     }
}

Odpowiednik haków

useEffect(() => {
     // action here
}, [props.counter]); // checks for changes in the values in this array

Jeśli włączysz tę tablicę, pamiętaj, aby uwzględnić wszystkie wartości z zakresu komponentu, które zmieniają się w czasie (właściwości, stan), w przeciwnym razie możesz odwołać się do wartości z poprzednich renderów.

Istnieją pewne subtelności w używaniu useEffect; sprawdź API Here.


Przed wersją 16.7.0

Właściwością komponentów funkcji jest to, że nie mają dostępu do funkcji cyklu życia Reacts ani thissłowa kluczowego. Musisz przedłużyć React.Componentklasę, jeśli chcesz korzystać z funkcji cyklu życia.

class Grid extends React.Component  {
    constructor(props) {
       super(props)
    }

    componentDidMount () {
        if(!this.props.fetched) {
            this.props.fetchRules();
        }
        console.log('mount it!');
    }
    render() {
    return(
        <Content title="Promotions" breadcrumbs={breadcrumbs} fetched={skuRules.fetched}>
            <Box title="Sku Promotion">
                <ActionButtons buttons={actionButtons} />
                <SkuRuleGrid 
                    data={skuRules.payload}
                    fetch={props.fetchSkuRules}
                />
            </Box>      
        </Content>  
    )
  }
}

Komponenty funkcyjne są przydatne, gdy chcesz renderować tylko komponent bez potrzeby stosowania dodatkowej logiki.

Shubham Khatri
źródło
1
Jak powiedziałem, masz logikę w swoim komponencie i wymagasz, abyś używał funkcji cyklu życia, a nie możesz tego zrobić z komponentami funkcyjnymi. Więc lepiej wykorzystaj klasę. Użyj komponentu funkcjonalnego, gdy twój komponent nie zawiera dodatkowej logiki
Shubham Khatri,
1
Należy zauważyć, że nie jest to dokładny odpowiednik elementu componentDidUpdate. useEffect(() => { // action here }, [props.counter])jest uruchamiany przy pierwszym renderowaniu, podczas gdy składnik componentDidUpdate nie.
Estus Flask
1
passing an empty array as second argument triggers the callback in useEffect only after the initial renderbrzmi to jak brudny, hakerski sposób na budowanie rzeczy: / miejmy nadzieję, że zespół reagujący wymyśli coś lepszego w przyszłych wydaniach.
Lukas Liesis
3
więc? gdzie jest część, w której odpowiadasz, jak uruchomić kod na componentwillmount?
Toskan
59

Możesz użyć cyklu życia w czystej postaci, aby dodać funkcje cyklu życia do komponentów funkcjonalnych.

Przykład:

import React, { Component } from 'react';
import lifecycle from 'react-pure-lifecycle';

const methods = {
  componentDidMount(props) {
    console.log('I mounted! Here are my props: ', props);
  }
};

const Channels = props => (
<h1>Hello</h1>
)

export default lifecycle(methods)(Channels);
Yohann
źródło
3
Co to jest Grid? Nie widzę tego w żadnym miejscu we fragmencie kodu? Jeśli i ty chciałbyś użyć do tego reduxu, czy ujdzie ci to na sucho export default lifecycle(methods)(connect({},{})(ComponentName))?
Sean Clancy
@SeanClancy Przepraszamy za spóźnioną odpowiedź. Fragment kodu został zaktualizowany.
Yohann,
1
Czy jest to uważane za dobrą praktykę? Czy powinienem wypróbować różne rozwiązania, zanim sięgnę po to, czy mogę z niego skorzystać, jeśli uznam to za najłatwiejsze?
SuperSimplePimpleDimple
9

Rozwiązanie pierwsze: Możesz użyć nowego interfejsu API React HOOKS . Obecnie w React v16.8.0

Hooki pozwalają ci używać więcej funkcji Reacta bez klas. Hooki zapewniają bardziej bezpośrednie API dla koncepcji Reacta, które już znasz: właściwości, stanu, kontekstu, odnośników i cyklu życia . Hooki rozwiązują wszystkie problemy rozwiązane przez Recompose.

Uwaga od autora recompose(acdlite, 25 października 2018):

Cześć! Stworzyłem Recompose jakieś trzy lata temu. Mniej więcej rok później dołączyłem do zespołu React. Dzisiaj ogłosiliśmy propozycję dotyczącą hooków. Hooki rozwiązują wszystkie problemy, które próbowałem rozwiązać trzy lata temu w Recompose, a nawet więcej. Będę przerywał aktywną opiekę nad tym pakietem (z wyłączeniem być może poprawek błędów lub łatek dla kompatybilności z przyszłymi wydaniami Reacta) i zalecał ludziom używanie hooków. Twój istniejący kod z Recompose będzie nadal działał, po prostu nie oczekuj żadnych nowych funkcji.

Rozwiązanie drugie:

Jeśli używasz wersji recomposeReact , która nie obsługuje haków, nie martw się, zamiast tego użyj (Pas narzędziowy React dla komponentów funkcyjnych i komponentów wyższego rzędu). Możesz użyć recomposedo dołączenia lifecycle hooks, state, handlers etcdo komponentu funkcji.

Oto komponent bez renderowania, który dołącza metody cyklu życia za pośrednictwem HOC cyklu życia (z rekompozycji).

// taken from https://gist.github.com/tsnieman/056af4bb9e87748c514d#file-auth-js-L33

function RenderlessComponent() {
  return null; 
}

export default lifecycle({

  componentDidMount() {
    const { checkIfAuthed } = this.props;
    // Do they have an active session? ("Remember me")
    checkIfAuthed();
  },

  componentWillReceiveProps(nextProps) {
    const {
      loadUser,
    } = this.props;

    // Various 'indicators'..
    const becameAuthed = (!(this.props.auth) && nextProps.auth);
    const isCurrentUser = (this.props.currentUser !== null);

    if (becameAuthed) {
      loadUser(nextProps.auth.uid);
    }

    const shouldSetCurrentUser = (!isCurrentUser && nextProps.auth);
    if (shouldSetCurrentUser) {
      const currentUser = nextProps.users[nextProps.auth.uid];
      if (currentUser) {
        this.props.setCurrentUser({
          'id': nextProps.auth.uid,
          ...currentUser,
        });
      }
    }
  }
})(RenderlessComponent);
Shivam
źródło
4

Możesz tworzyć własne metody cyklu życia.

Funkcje użytkowe

import { useEffect, useRef } from "react";

export const componentDidMount = handler => {
  return useEffect(() => {
    return handler();
  }, []);
};

export const componentDidUpdate = (handler, deps) => {
  const isInitialMount = useRef(true);

  useEffect(() => {
    if (isInitialMount.current) {
      isInitialMount.current = false;

      return;
    }

    return handler();
  }, deps);
};

Stosowanie

import { componentDidMount, componentDidUpdate } from "./utils";

export const MyComponent = ({ myProp }) => {
  componentDidMount(() => {
    console.log("Component did mount!");
  });

  componentDidUpdate(() => {
    console.log("Component did update!");
  });

  componentDidUpdate(() => {
    console.log("myProp did update!");
  }, [myProp]);
};  
Etienne Martin
źródło
2

Zgodnie z dokumentacją:

import React, { useState, useEffect } from 'react'
// Similar to componentDidMount and componentDidUpdate:

useEffect(() => {


});

zobacz dokumentację React

DevB2F
źródło
0

Jeśli potrzebujesz użyć React LifeCycle, musisz użyć Class.

Próba:

import React, { Component } from 'react';

class Grid extends Component {

 constructor(props){
  super(props)
 }

 componentDidMount () { /* do something */ }

 render () { 
   return <h1>Hello</h1>
 }

}
Gabriel Ferreira
źródło
2
Nie chcę korzystać z klasy.
Aftab Naveed
3
Pytanie brzmiało, jak używać metod cyklu życia z komponentem funkcjonalnym, a nie klasą.
Mike,
Teraz z React Hooks
Gabriel Ferreira,
0

Możesz skorzystać z modułu create-react-class. Oficjalna dokumentacja

Oczywiście musisz go najpierw zainstalować

npm install create-react-class

Oto działający przykład

import React from "react";
import ReactDOM from "react-dom"
let createReactClass = require('create-react-class')


let Clock = createReactClass({
    getInitialState:function(){
        return {date:new Date()}
    },

    render:function(){
        return (
            <h1>{this.state.date.toLocaleTimeString()}</h1>
        )
    },

    componentDidMount:function(){
        this.timerId = setInterval(()=>this.setState({date:new Date()}),1000)
    },

    componentWillUnmount:function(){
        clearInterval(this.timerId)
    }

})

ReactDOM.render(
    <Clock/>,
    document.getElementById('root')
)
Chandan Purohit
źródło
0

jeśli używasz reakcji 16.8, możesz użyć hooków reakcji ... Haczyki reakcji to funkcje, które pozwalają na „podpięcie się” do stanu reakcji i funkcji cyklu życia z komponentów funkcji ... docs

WAEX
źródło