Next.js Discord

Discord Forum

How do I get response data from the serverComponent back into the clientComponent?

Answered
Sloth bear posted this in #help-forum
Open in Discord
Sloth bearOP
So I'm modifying our entire app so that all Fetch requests go through server components and not client components so that the API requests / keys etc are all hidden from the browser, and solve the CORS issue with out a Node server.

I have my page.tsx which is using Server actions to fetch form data from my Login form client component, and I'm able to Post a fetch request successfully now.

However the problems I have now in the Server Component, is that

1) I cannot store certain data in the localStorage (various tokens)
2) I cannot navigate using Link to the correct dashboard view
3) I cannot pass in a call back from the server component into the client component to handle the logic there.

What is the pattern I should use in this situation?

## Client component (The Form)
'use client';

const LoginForm = ({ action }: any) => {
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault(); // Prevent default form submission

    const formData = new FormData(e.currentTarget);
    await action(formData); // Invoke the action function with formData

    console.log('Form submitted');
    // console.log(formData);
  };

  return (
    <>
      <form onSubmit={handleSubmit}>
        <div>
          <label htmlFor="email">Email</label>
          <input
            type="email"
            name="email"
            id="email"
            placeholder="Email Address"
            required
          />
        </div>
        <div>
          <label htmlFor="password">Password:</label>
          <input
            type="password"
            name="password"
            id="password"
            placeholder="Password"
            required
          />
        </div>
        <div className="actions">
          <button type="submit">Submit</button>
        </div>
      </form>
    </>
  );
};

export default LoginForm;


## My server (login/page.tsx) component

import LoginForm from '@/components/auth/login-form';
import { isValidEmail } from '@/utils/validators';
import {
  API_KEY,
  API_KEY_NAME,
  API_LOGIN,
} from '@/constants';

if (!API_LOGIN) {
  throw new Error('API URL is not defined');
}

const LoginPage = () => {
  async function myFormProcessor(formData: any) {
    'use server';
    const email = formData.get('email');
    const password = formData.get('password');

    if (!isValidEmail(email)) {
      throw new Error('Error: 400 - Invalid email address');
    }

    console.log('API_KEY', API_KEY);

    const payload = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        [API_KEY_NAME]: API_KEY,
      },
      body: JSON.stringify({ user: email, password }),
    };

    console.log('payload', payload);

    try {
      const response = await fetch(API_LOGIN, payload);

      console.log('Auth response:', response);

      if (!response.ok) {
        throw new Error('Failed to fetch');
      }

      const data = await response.json();

      console.log('TIME TO REDIRECT TO /POSTINGS');
      // router.push('/postings');
    } catch (error) {
      console.error(error);
    }
  }

  return (
    <div id="auth-container">
      <Link href="/">
        <h1 className="logo">
          Bounty<strong>Jobs</strong>
        </h1>
      </Link>
      <LoginForm action={myFormProcessor} />
    </div>
  );
};

export default LoginPage;
Answered by Ray
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault(); // Prevent default form submission

    const formData = new FormData(e.currentTarget);
    const data = await action(formData); // get response data from server action

    // console.log(formData);

     if (data) {
      console.log('Form submitted');

      localStorage.setItem('data', JSON.stringity(data)) // store the data to localStorage

      router.push("/") // handle navigation
    }
  };
View full answer

9 Replies

Brown bear
https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations

Try using form action instead of onSubmit, and use server actions as the action.
@Sloth bear So I'm modifying our entire app so that all Fetch requests go through **server components** and not **client components** so that the API requests / keys etc are all hidden from the browser, and solve the CORS issue with out a Node server. I have my page.tsx which is using Server actions to fetch form data from my Login form client component, and I'm able to Post a fetch request successfully now. However the problems I have now in the Server Component, is that 1) I cannot store certain data in the localStorage (various tokens) 2) I cannot navigate using Link to the correct dashboard view 3) I cannot pass in a call back from the server component into the client component to handle the logic there. What is the pattern I should use in this situation? ## Client component (The Form) 'use client'; const LoginForm = ({ action }: any) => { const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); // Prevent default form submission const formData = new FormData(e.currentTarget); await action(formData); // Invoke the action function with formData console.log('Form submitted'); // console.log(formData); }; return ( <> <form onSubmit={handleSubmit}> <div> <label htmlFor="email">Email</label> <input type="email" name="email" id="email" placeholder="Email Address" required /> </div> <div> <label htmlFor="password">Password:</label> <input type="password" name="password" id="password" placeholder="Password" required /> </div> <div className="actions"> <button type="submit">Submit</button> </div> </form> </> ); }; export default LoginForm; ## My server (login/page.tsx) component import LoginForm from '@/components/auth/login-form'; import { isValidEmail } from '@/utils/validators'; import { API_KEY, API_KEY_NAME, API_LOGIN, } from '@/constants'; if (!API_LOGIN) { throw new Error('API URL is not defined'); } const LoginPage = () => { async function myFormProcessor(formData: any) { 'use server'; const email = formData.get('email'); const password = formData.get('password'); if (!isValidEmail(email)) { throw new Error('Error: 400 - Invalid email address'); } console.log('API_KEY', API_KEY); const payload = { method: 'POST', headers: { 'Content-Type': 'application/json', [API_KEY_NAME]: API_KEY, }, body: JSON.stringify({ user: email, password }), }; console.log('payload', payload); try { const response = await fetch(API_LOGIN, payload); console.log('Auth response:', response); if (!response.ok) { throw new Error('Failed to fetch'); } const data = await response.json(); console.log('TIME TO REDIRECT TO /POSTINGS'); // router.push('/postings'); } catch (error) { console.error(error); } } return ( <div id="auth-container"> <Link href="/"> <h1 className="logo"> Bounty<strong>Jobs</strong> </h1> </Link> <LoginForm action={myFormProcessor} /> </div> ); }; export default LoginPage;
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault(); // Prevent default form submission

    const formData = new FormData(e.currentTarget);
    const data = await action(formData); // get response data from server action

    // console.log(formData);

     if (data) {
      console.log('Form submitted');

      localStorage.setItem('data', JSON.stringity(data)) // store the data to localStorage

      router.push("/") // handle navigation
    }
  };
Answer
@Sloth bear So I'm modifying our entire app so that all Fetch requests go through **server components** and not **client components** so that the API requests / keys etc are all hidden from the browser, and solve the CORS issue with out a Node server. I have my page.tsx which is using Server actions to fetch form data from my Login form client component, and I'm able to Post a fetch request successfully now. However the problems I have now in the Server Component, is that 1) I cannot store certain data in the localStorage (various tokens) 2) I cannot navigate using Link to the correct dashboard view 3) I cannot pass in a call back from the server component into the client component to handle the logic there. What is the pattern I should use in this situation? ## Client component (The Form) 'use client'; const LoginForm = ({ action }: any) => { const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); // Prevent default form submission const formData = new FormData(e.currentTarget); await action(formData); // Invoke the action function with formData console.log('Form submitted'); // console.log(formData); }; return ( <> <form onSubmit={handleSubmit}> <div> <label htmlFor="email">Email</label> <input type="email" name="email" id="email" placeholder="Email Address" required /> </div> <div> <label htmlFor="password">Password:</label> <input type="password" name="password" id="password" placeholder="Password" required /> </div> <div className="actions"> <button type="submit">Submit</button> </div> </form> </> ); }; export default LoginForm; ## My server (login/page.tsx) component import LoginForm from '@/components/auth/login-form'; import { isValidEmail } from '@/utils/validators'; import { API_KEY, API_KEY_NAME, API_LOGIN, } from '@/constants'; if (!API_LOGIN) { throw new Error('API URL is not defined'); } const LoginPage = () => { async function myFormProcessor(formData: any) { 'use server'; const email = formData.get('email'); const password = formData.get('password'); if (!isValidEmail(email)) { throw new Error('Error: 400 - Invalid email address'); } console.log('API_KEY', API_KEY); const payload = { method: 'POST', headers: { 'Content-Type': 'application/json', [API_KEY_NAME]: API_KEY, }, body: JSON.stringify({ user: email, password }), }; console.log('payload', payload); try { const response = await fetch(API_LOGIN, payload); console.log('Auth response:', response); if (!response.ok) { throw new Error('Failed to fetch'); } const data = await response.json(); console.log('TIME TO REDIRECT TO /POSTINGS'); // router.push('/postings'); } catch (error) { console.error(error); } } return ( <div id="auth-container"> <Link href="/"> <h1 className="logo"> Bounty<strong>Jobs</strong> </h1> </Link> <LoginForm action={myFormProcessor} /> </div> ); }; export default LoginPage;
and return data from your server action
Sloth bearOP
Thank you! Will try today 😄
Sloth bearOP
It doesn't like this
<form action={handleSubmit}>
{/* <form onSubmit={handleSubmit}> */}
And when I kept this, and had another successful login:

<form onSubmit={handleSubmit}>

This did not log out

const data = await action(formData); // get

    if (data) {
      console.log('Form submitted');
      console.log('data', data);
    }
@Sloth bear And when I kept this, and had another successful login: `<form onSubmit={handleSubmit}>` This did not log out const data = await action(formData); // get if (data) { console.log('Form submitted'); console.log('data', data); }
did you return the data from server action?
 try {
      const response = await fetch(API_LOGIN, payload);

      console.log('Auth response:', response);

      if (!response.ok) {
        throw new Error('Failed to fetch');
      }

      const data = await response.json();

      console.log('TIME TO REDIRECT TO /POSTINGS');
      return data
      // router.push('/postings');
    } catch (error) {
      console.error(error);
    }
Sloth bearOP
ah did forget that
Yes I get it back in the client now! 😄