risc
risc

Reputation: 11

How to share a state variable between a client component and a server component in Nextjs?

My first component is a server-component holding a list of items which I load from a database. This list should be rendered server-side because of seo. My second component is a client-component holding an input field.

When the user types something in the input field, I want to filter the item list regarding to the input.

I can't find a way to share the input state between the two components so the component holding the item list can be a server-component. If that's not possible is there a way to make it a client component so it is still seo friendly?

Probably there will be a very general solution for this type of problem, but I am not that long in nextjs development and this is the first time I encountered it :/

I tried to break the problem down to the minimum.

So I have this component holding an inputField and outputField:

const SharedContextVariable = () => {
return (
    <div className='w-full h-screen flex flex-col gap-5 justify-center items-center'>
        <InputField />
        <OutputField />
    </div>
)

}

This is the inputField:

const InputField = () => {
const [input, setInput] = useState("");

return (
    <input className='text-black p-2' placeholder='input' value={input} onChange={(e) => setInput(e.target.value)} />
)

}

And this is the outputField:

const OutputField = () => {
return (
    <div className='w-1/2 h-64 border-[1px] border-white flex justify-center items-center'>
        <p className='text-white '>
            {/* input should be displayed here */}
        </p>
    </div>
)

}

How to display the input of the inputField in the outputField in realtime while remaining it as a server-component (if that is possible)?

Upvotes: 1

Views: 1903

Answers (1)

risc
risc

Reputation: 11

After some research I found out by myself. The solution is to use url based props.

Here is the extended example code so it has the expected behaviour.

This is the parent component (page.tsx):

import React from 'react'
import InputField from './components/inputField'
import OutputField from './components/outputField'

type Props = {
   searchParams: { [key: string]: string | string[] | undefined },
}

const SharedState = (props: Props) => {
const searchParams = props.searchParams;

return (
    <div className='w-full h-screen flex flex-col gap-5 justify-center 
items-center'>
        <InputField />
        <OutputField searchParams={searchParams} />
    </div>
)
}

export default SharedState

The client-component (inputField.tsx):

'use client';

import React, { useState, useEffect } from 'react'
import { useDebounce } from 'use-debounce';
import { useRouter } from 'next/navigation';

const InputField = () => {
const [input, setInput] = useState("");
const [query] = useDebounce(input, 500);
const router = useRouter();

useEffect(() => {
    if (!query) {
        router.push('/sharedState');
    }
    else {
        router.push(`/sharedState?query=${query}`)
    }
}, [query]);

return (
    <input
        className='text-black p-2'
        placeholder='...'
        value={input}
        onChange={(e) => setInput(e.target.value)}
    />
)
}

export default InputField

The server-component (outputField.tsx):

type Props = {
searchParams: { [key: string]: string | string[] | undefined };
};

const OutputField = (props: Props) => {
const searchParams = props.searchParams;
const query = searchParams.query;

return (
    <div className='w-1/2 h-64 border-[1px] border-white flex justify-        
center items-center'>
        <p className='text-white '>
            {query}
        </p>
    </div>
)
}

export default OutputField

Upvotes: 0

Related Questions