1

I basically have a button element pretty far down the component hierarchy so I'm passing a function from my App level downwards so that it can be called onClick within the button. I feel like I've correctly defined both the App function (newTodoCard()) as :()=>void and the prop of child component (onAddTodo()) as :()=>void. I would prefer to avoid defining an entire Interface for one function prop and want to understand why my approach isn't working.

App.tsx

import React from 'react';
import Header from './components/Header';
import Sidebar from './components/Sidebar';
import TodoCard from './components/TodoCard';
import { TodoCardProps } from './components/TodoCard';
import { useState } from 'react';

function App() {
  const[todoCards,setTodoCards] = useState([]);
  let currentTodoCard: TodoCardProps = {title:"",content:""};
  const newTodoCard: ()=>void = ()=>{
    currentTodoCard = {title:"NEW",content:"NEW"};
  }
  return (
    <div className="App">
      <Header/>
      <div className="container">
        <Sidebar {...newTodoCard}/>
        <TodoCard {...currentTodoCard}/>
      </div>
    </div>
  );
}

export default App;

This snippet from above is where the error is:

<Sidebar {...newTodoCard}/>

Sidebar.tsx

import React from 'react'
import TitleCards from './TitleCards'
import SidebarHeader from './SidebarHeader'

const Sidebar = ( onAddTodo: ()=>void) => {
  return (
    <div className="sidebar">
        <SidebarHeader {...onAddTodo}/>
        <TitleCards/>
    </div>
  )
}

export default Sidebar

Additionally, if I change how I pass in the prop to

<Sidebar onAddTodo={newTodoCard}/>

It seems to solve the issue, but this error Type '{ onAddTodo: () => void; }' is not assignable to type 'IntrinsicAttributes & (() => void)'. Property 'onAddTodo' does not exist on type 'IntrinsicAttributes & (() => void)' appears (which from online research is only fixed by using {...prop} as I did originally. I appreciate any help! Thanks.

1 Answer 1

1

The specific error you are seeing is because you are trying to use a props spread operator ... on something that isn't an object. newTodoCard is a function. If you want to pass it to <SidebarHeader /> as a prop, you can just do <SidebarHeader onAddTodo={newTodoCard} />.

You would declare a prop in your SidebarHeader component to match the callback that is being passed. The key thing to note here is that the props that are passed to your component is an object with a property called onAddTodo.

interface MyProps {
    onAddTodo: () => void;
}

export function SidebarHeader = (props: MyProps) => {

    return <div>
        <Button onClick={event => props.onAddTodo()} />
    </div>
}

However, your callback isn't going to work as expected the way you have written it. When it is called, it is going to set the value of currentTodoCard, but that is a local variable that has a limited scope. The next time your app re-renders, the App function is going to be called again, and you will get a new instance of currentTodoCard. In order for your callback to work as expected, it is going to need to call setTodoCards or set some other state.

Sign up to request clarification or add additional context in comments.

7 Comments

Okay, I really appreciate you pointing out that that var will be reset and I need to manage some state. Also thanks for pointing out that I can't use spread here, but when I try the alternative of <SidebarHeader myCallback={newTodoCard} /> I get this error in both App.tsx and Sidebar.tsx: Type '{ onAddTodo: () => void; }' is not assignable to type 'IntrinsicAttributes & (() => void)'. Property 'onAddTodo' does not exist on type 'IntrinsicAttributes & (() => void)'.
That error is because the type annotation on your sidebar props is not right. You will actually be passed an object with the onAddTodo method, not the method directly.
You can do something like this: tsx const Sidebar = ( props: { onAddTodo: ()=>void }) => { return ( <div className="sidebar"> <SidebarHeader {...props}/> <TitleCards/> </div> ) } Now the props spread operator is allowed since props is an object.
Wow. Fixed everything. Did both the interface method you showed above and this spread one and they do the trick. One final question: is it ever possible to purely pass a method or is always passed as like the object? I really appreciate all the help Matt!
Awesome thanks. I will look into Context because it feels weird to keep like passing the same prop through a bunch of intermediary children that don't really use it and just pass it. I'm brand new to frontend and React/TS so thanks again, Matt!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.