Guy Kohn
Guy Kohn

Reputation: 11

how to access a custom react component function from its parent component?

I would like to create a new instance of a component and then use a function within that component to add add a new object. Nothing works for me. Here is my code:

The component that holds the list and the function to add new eventCard

function EventDay (props) { 

   const [ events, setEvents ] = useState([{eventName: "first event"}]);

   function addEvent(eventCard) {
      setEvents( prevEvents => [...prevEvents, eventCard]);
   }

   return console.log(events);
}

export default EventDay;

the component that wants to access the addEvent function

import EventDay from "./EventDay"

function App() {
   const eventDay = new EventDay();

   eventDay.addEvent({eventName: "New Event"});

}

I get an error addEvent is not a function. I tried to export it but I can't since it's a function within a function. How can I achieve the above?

Upvotes: 1

Views: 95

Answers (1)

Ori Drori
Ori Drori

Reputation: 191976

Hooks can only be use inside functional components, and they cannot be used as constructors. The standard answer would be to move the state handling to the wrapping component (App), and pass the state to EventDay:

const { useState, useCallback, useEffect } = React;

const EventDay = ({ events }) => events.map((o, i) => (<li key={i}>{o.eventName}</li>));

function App() {
  const [events, setEvents] = useState([{eventName: "first event"}]);
  
  const addEvent = useCallback(eventCard => setEvents(prevEvents => [...prevEvents, eventCard]), []);
  
  useEffect(() => {
    setTimeout(() => {
      addEvent({eventName: "new event"});
    }, 1000);
  }, [addEvent])
  
  return (
    <ul>
      <EventDay events={events} />
    </ul>
  );
}

ReactDOM.render(
  <App />,
  root
)
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

However, if you must get a function from the component, you can use ref with a the useImperativeHandle hook:

const { forwardRef, useState, useImperativeHandle, useRef, useEffect } = React;

const EventDay = forwardRef((props, ref) => { 
  const [events, setEvents] = useState([{eventName: "first event"}]);

  useImperativeHandle(ref, () => ({
    addEvent(eventCard) {
      setEvents(prevEvents => [...prevEvents, eventCard]);
    }
  }));

  return events.map((o, i) => (<li key={i}>{o.eventName}</li>));
});

function App() {
  const eventRef = useRef();
  
  useEffect(() => {
    setTimeout(() => {
      eventRef.current.addEvent({eventName: "new event"});
    }, 1000);
  }, [])
  
  return (
    <ul>
      <EventDay ref={eventRef} />
    </ul>
  );
}

ReactDOM.render(
  <App />,
  root
)
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

Upvotes: 1

Related Questions