I'm having some trouble getting NextJS to work with props and passing them through components. This may be a bit more of a React approach so maybe it doesn't work here but I've tried googling and not many results come back with how to do it. Basically what I'm trying to do is simply pass a useState and setUseState value from my parent to child in order to update inside the child onClick.
However there is an error that seems to be occurring on the Prop type when I pass props through. The only way to avoid it seems to be by using (props: any)
however I don't want to do this as it isn't Type safe.
Is what I'm trying to do possible in NextJS or is there a different way to do it? I had a look at getInitial/Static/ServerProps
but couldn't get those to work either.
Admittedly I haven't got much experience with Next so perhaps it's a skill issue.
Hopefully that was clear enough, happy to add more detail I probably missed something crucial it's just been plaguing me all day!
Thanks for any help.
Parent component
'use client';
import { useState } from 'react';
import ChildComponent from './childComponent/page';
export default function ParentComponent() {
const [value, setValue] = useState('hello world')
return <ChildComponent value={value} setValue={setValue}/>
}
Child component
import React, { Dispatch, SetStateAction } from 'react'
interface Props {
value: string
setValue: Dispatch<SetStateAction<string>>
}
const ChildComponent = (props: Props) => {
return (
<div>
<span onClick={() => props.setValue('hello moon')}>{props.value}</span>
</div>
)
}
export default ChildComponent
Error
.next/types/app/childComponent/page.ts:26:13
Type error: Type 'OmitWithTag<Props, keyof PageProps, "default">' does not satisfy the constraint '{ [x: string]: never; }'.
Property 'value' is incompatible with index signature.
Type 'string' is not assignable to type 'never'.
24 |
25 | // Check the prop type of the entry function
> 26 | checkFields<Diff<PageProps, FirstArg<TEntry['default']>, 'default'>>()
| ^
layout.tsx
import ...
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'Title',
description: 'Generated by create next app',
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className=' bg-pink-300 h-screen w-screen content-center justify-center items-center flex'>
<body className={`${inter.className} h-screen w-screen justify-center items-center flex`}>
{children}
</body>
</html>
)
}
next.config.js is default
I have tried with const Component = (props: Props)
, const Component: React.FC<Props> = (props)
, Interface/Type { value: any, setValue: any }
and all permutations of these to no avail.
The app also builds perfectly fine when running npm run dev and the usestate can be updated but just when running npm run build does it fail.
To the best of my knowledge it looks like the only types that can be passed are those that can be made are those that are valid in an index interface An index signature parameter type must be 'string', 'number', 'symbol', or a template literal type.
interface Props {
[key: string | number ]: string
}
Because of this I am going to leave it as (props: any) for now...
Edit 2:
Attempted with the components folder mentioned by Sachin and it's working perfectly. Perhaps you can do it the other way too but my brain is too small or it's just not possible.
Here is the classes and dir layout for anyone's future reference...
Parent
'use client';
import { useState } from 'react'
import { ChildComponent } from '@/components/';
export default function Page() {
const [value, setValue] = useState('hello world')
return <ChildComponent setValue={setValue} value={value} />
}
import React, { Dispatch, SetStateAction } from 'react'
interface Props {
value: string
setValue: Dispatch<SetStateAction<string>>
}
const ChildComponent = (props: Props) => {
return (
<div>
<span onClick={() => props.setValue('hello moon')}>{props.value}</span>
</div>
)
}
export default ChildComponent
Thanks to all!
Solution:
Use the components folder, the directory layout should be like
root
|
├- app
| ├ page.tsx
| ├ layout.tsx
| └ ...
|
├- components
| ├ childComponent
| | └ ChildComponent.tsx
| └ index.ts (optional)
└ ...
Page.tsx it should simply be:
'use client';
import { useState } from 'react'
import { ChildComponent } from '@/components/';
export default function Page() {
const [value, setValue] = useState('hello world')
return <ChildComponent setValue={setValue} value={value} />
}
ChildComponent.tsx (with props):
import React, { Dispatch, SetStateAction } from 'react'
interface Props {
value: string
setValue: Dispatch<SetStateAction<string>>
}
const ChildComponent = (props: Props) => {
return (
<div>
<span onClick={() => props.setValue('hello moon')}>{props.value}</span>
</div>
)
}
export default ChildComponent
See my original question for more detail if needed!