skip to Main Content
import React, { useEffect, useRef, useState } from 'react';
import $ from 'jquery';
import 'jquery.terminal/css/jquery.terminal.min.css';
import 'jquery.terminal/js/jquery.terminal.min.js';
import { useRouter } from 'next/router';

const TerminalProfile = ({ width, height }) => {
  const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
  const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
  const [time, setTime] = useState(getFormattedTime());
  const terminalRef = useRef(null);
  const [started, setStarted] = useState(false);
  const router = useRouter();
  const [userData, setUserData] = useState({
    Name: 'Yiğitcan',
    Surname: 'Uçar',
    Email: '[email protected]',
    Job: 'Computer Engineer',
    City: 'Istanbul',
    Country: 'Turkey',
    Phone: '+90 555 555 55 55',
    Hobbies: 'Programming, Music, Reading, Gaming'
  });

  useEffect(() => {
    const terminal = $(terminalRef.current);

    terminal.terminal(
      async (command, term) => {
        if (command === 'my_profile' && !started) {
          setStarted(true);
          await startLoadingAnimation(term);
          startAnimation(term, 0);
        } else if (command === 'logout') {
          term.echo('Logging out...');
          setTimeout(() => {
            term.echo('[[;red;]Redirecting to login page...]');
            setTimeout(() => {
              router.push('/auth/login'); // Next.js'in yönlendirme metodu
            }, 1000);
          }, 1000);
        } else if (command === 'progress') {
          showLoadingAnimation(term);
        } else if (command === 'data') {
          console.log(userData);
        }
         else if (command === 'help') {
          startHelping(term, 0); // Yardım animasyonunu başlatır
        } else if (command.startsWith('Update')) {
          const newData = parseUpdateCommand(command);
          updateUserData(term, newData);
        } else {
          term.typing('echo', 20, `Command not recognized: ${command}`);
        }
      },
      {
        greetings: () => `Current time is: ${time} on this shellnPlease type 'help' for more information.`,
        name: 'my_terminala',
        prompt: '(base) leatherfire@Yigit-MacBook-Pro ~ % ',
        css: {
          'background-color': 'white',
          color: 'black'
        }
      }
    );
  }, [userData]);

  useEffect(() => {
    $(terminalRef.current).css({ width: width + 'px', height: (height - 3) + 'px' });
  }, [width, height]);

  useEffect(() => {
    const interval = setInterval(() => {
      setTime(getFormattedTime());
    }, 1000);

    return () => clearInterval(interval);
  }, [getFormattedTime]);

  async function startLoadingAnimation(term) {
    term.typing('echo', 20, 'Data fetching from the System of WebOS...');
    await new Promise(resolve => setTimeout(resolve, 800));
    showLoadingAnimation(term);
    await new Promise(resolve => setTimeout(resolve, 11000)); // Simulate data fetching delay
  }

  function showLoadingAnimation(term) {
    const size = 30;
    const prompt = term.get_prompt();
    let string = progress(0, size);
    term.set_prompt(string);
    let i = 0;
    let animation = true; // Değişken var olan "const" tanımından "let" olarak değiştirildi.
    (function loop() {
      string = progress(i++, size);
      term.set_prompt(string);
      if (i < 100) {
        const timer = setTimeout(loop, 100);
      } else {
        term.echo(progress(i, size) + ' [[b;green;]OK]')
          .set_prompt(prompt);
        animation = false;
      }
    })();
  }

  function startAnimation(term, step) {
    const newUser = {...userData}; // Change userData to newUser
    const userDataKeys = Object.keys(newUser); // Use newUser here
    if (step < userDataKeys.length) {
      const key = userDataKeys[step];
      const currentStep = newUser[key]; // Use newUser here
      term.typing('echo', 50, `${key} ==> ${currentStep}`, () => {
        startAnimation(term, step + 1, newUser); // Use newUser here
      });
    } else {
      term.set_prompt('(base) leatherfire@Yigit-MacBook-Pro ~ % ');
      setStarted(false);
    }
  }
  
  
  function startHelping(term, step) {
    const steps = [
      { command: 'my_profile :', response: `==> Displays your profile.` },
      { command: 'UpdateName <name> :', response: '==> Name updated successfully to your name.' },
      { command: 'UpdateSurname <surname> :', response: '==> Surname updated successfully to your surname.' },
      { command: 'UpdateEmail <email> :', response: '==> Email updated successfully to your email.' },
      { command: 'UpdateJob <job> :', response: '==> Job updated successfully to your job.' },
      { command: 'UpdateCity <city> :', response: '==> City updated successfully to your city.' },
      { command: 'UpdateCountry <country> :', response: '==> Country updated successfully to your country.' },
      { command: 'UpdatePhone <phone> :', response: '==> Phone updated successfully to your phone.' },
      { command: 'UpdateHobbies <hobbies> :', response: '==> Hobbies updated successfully to your hobbies.' },
      { command: 'clear :', response:   '==> Clears the terminal.' },
      { command: 'help :', response: '==> This is the help command.' },
      { command: 'logout :', response: '==> Logs out of the profile.' },
    ];

    if (step < steps.length) {
      const currentStep = steps[step];
      term.typing('echo', 50, currentStep.command, () => {
        term.typing('echo', 50, currentStep.response, () => {
          startHelping(term, step + 1);
        });
      });
    } else {
      term.set_prompt('(base) leatherfire@Yigit-MacBook-Pro ~ % ');
    }
  }

  function updateUserData(term, newData) {
    setUserData(newData);
    console.log("newData",newData,"nuserData",userData);
    term.typing('echo', 50, `User data updated successfully`);
  }
    
  function parseUpdateCommand(command) {
    const parts = command.split(' ');
    const field = parts[0].replace('Update', ''); // Extract field name
    const value = parts.slice(1).join(' '); // Extract value
    return { ...userData, [field]: value }; // Merge with existing user data
  }

  function getFormattedTime() {
    const now = new Date();
    const dayIndex = now.getDay();
    const dayOfWeek = days[dayIndex];
    const dayOfMonth = now.getDate();
    const monthIndex = now.getMonth();
    const month = months[monthIndex];
    const year = now.getFullYear();
    const hour = now.getHours();
    const minute = now.getMinutes();
    const second = now.getSeconds();

    return `${dayOfWeek} ${month} ${dayOfMonth} ${year} ${hour}:${minute}:${second}`;
  }

  function progress(percent, width) {
    const size = Math.round(width * percent / 100);
    let taken = '', i;
    for (i = size; i--;) {
      taken += '=';
    }
    if (taken.length > 0) {
      taken = taken.replace(/=$/, '>');
    }
    let left = '';
    for (i = width - size; i--;) {
      left += ' ';
    }
    return '[' + taken + left + '] ' + percent + '%';
  }

  return (
    <div className='rounded-b-xl'
      ref={terminalRef}
      style={{
        width: width + 'px',
        height: height + 'px',
        overflow: 'auto',
        padding: '1px',
        background: 'white',
        color: '#000'
      }}
    />
  );
};

export default TerminalProfile;

I have a custom terminal component built with React that simulates a command-line interface. The component displays and updates user profile data using a command like "UpdateName ." However, despite the new data object reflecting the correct changes within the function handling the update, the state does not appear to update immediately, and the old profile values are still shown when accessing the data within other functions. It seems like there’s a closure issue, or the state isn’t updating correctly due to how useState is handled. Any advice on resolving this issue would be greatly appreciated.

This function:

function updateUserData(term, newData) {
    setUserData(newData);
    console.log("newData",newData,"nuserData",userData);
    term.typing('echo', 50, `User data updated successfully`);
  }

When I run this function, newData and userData are different, or in short, userData does not change at all and always remains at the initial values.

For example, by typing UpdateName turgut, the name in userData should be changed to turgut, but it remains Yiğitcan.

When I typed "UpdateName turgut", this appeared:

newData {Name: ‘turgut’, Surname: ‘Uçar’, Email: ‘[email protected]’, Job: ‘Computer Engineer’, City: ‘Istanbul’, …}

userData {Name: ‘Yiğitcan’, Surname: ‘Uçar’, Email: ‘[email protected]’, Job: ‘Computer Engineer’, City: ‘Istanbul’, …}

But to understand the problem I added code like this:

 useEffect(() => {
    console.log("nuseEffect",userData);
  }, [userData]);

and this time when I typed "updateName turgut", it wrote this on the console screen:

useEffect {Name: ‘Yiğitcan’, Surname: ‘Uçar’, Email: ‘[email protected]’, Job: ‘Computer Engineer’, City: ‘Istanbul’, …}

newData {Name: ‘turgut’, Surname: ‘Uçar’, Email: ‘[email protected]’, Job: ‘Computer Engineer’, City: ‘Istanbul’, …}

userData {Name: ‘Yiğitcan’, Surname: ‘Uçar’, Email: ‘[email protected]’, Job: ‘Computer Engineer’, City: ‘Istanbul’, …}

useEffect {Name: ‘turgut’, Surname: ‘Uçar’, Email: ‘[email protected]’, Job: ‘Computer Engineer’, City: ‘Istanbul’, …}

So it looks like it has changed, but for some reason the old one still appears in the function.
When this function I use to retrieve the data runs, it prints the old data:

function startAnimation(term, step) {
    const newUser = {...userData}; // Change userData to newUser
    const userDataKeys = Object.keys(newUser); // Use newUser here
    if (step < userDataKeys.length) {
      const key = userDataKeys[step];
      const currentStep = newUser[key]; // Use newUser here
      term.typing('echo', 50, `${key} ==> ${currentStep}`, () => {
        startAnimation(term, step + 1, newUser); // Use newUser here
      });
    } else {
      term.set_prompt('(base) leatherfire@Yigit-MacBook-Pro ~ % ');
      setStarted(false);
    }
  }

2

Answers


  1. Chosen as BEST ANSWER

    I try your solution but this time ı get this error:

    Error1

    I tried this code to solve this:

          React.useEffect(() => {
        function startAnimation(term, step) {
          const userDataKeys = Object.keys(userData);
          if (step < userDataKeys.length) {
            const key = userDataKeys[step];
            const currentStep = userData[key];
            term.echo(`${key} ==> ${currentStep}`, {
              typing: 50,
              finalize: () => {
                startAnimation(term, step + 1);
              }
            });
          } else {
            term.set_prompt('(base) leatherfire@Yigit-MacBook-Pro ~ % ');
            setStarted(false);
          }
        }
      }, [userData]);
    
    

    but this time I got this error:

    Error2


  2. The link @David provided in the comment should answer your question. I’ll try to add possible solutions:

    1. Provide fresh data to startAnimation as parameter: function startAnimation(term, step, newUser)
    2. Call startAnimation in a useEffect dependant on userData, which would probably look somewhat like this:
      React.useEffect(() => {
        function startAnimation(term, step) {
          const userDataKeys = Object.keys(userData); // Use newUser here
          if (step < userDataKeys.length) {
            const key = userDataKeys[step];
            const currentStep = userData[key]; // Use newUser here
            term.typing('echo', 50, `${key} ==> ${currentStep}`, () => {
              startAnimation(term, step + 1, userData); // Use newUser here
            });
          } else {
            term.set_prompt('(base) leatherfire@Yigit-MacBook-Pro ~ % ');
            setStarted(false);
          }
        }
        startAnimation(terminalRef.current, 0)
      }, [userData]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search