在自定义终端组件中 setState 后反应状态未正确更新

问题描述 投票:0回答:1
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 shell\nPlease 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;

我有一个用 React 构建的自定义终端组件,可以模拟命令行界面。该组件使用“UpdateName”等命令显示和更新用户配置文件数据。然而,尽管新数据对象反映了处理更新的函数内的正确更改,但状态似乎并未立即更新,并且在访问其他函数内的数据时仍然显示旧的配置文件值。似乎存在关闭问题,或者由于 useState 的处理方式而导致状态未正确更新。任何有关解决此问题的建议将不胜感激。

这个功能:

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

当我运行这个函数时,newData和userData是不同的,或者简而言之,userData根本没有改变,始终保持初始值。

例如,通过输入 UpdateName turgut,userData 中的名称应更改为 turgut,但它仍然是 Yiğitcan。

当我输入“UpdateName turgut”时,出现了:

newData {姓名:'turgut',姓氏:'Uçar',电子邮件:'[email protected]',工作:'计算机工程师',城市:'伊斯坦布尔',...}

userData {姓名:'Yiğitcan',姓氏:'Uçar',电子邮件:'[email protected]',工作:'计算机工程师',城市:'伊斯坦布尔',…}

但是为了理解这个问题,我添加了这样的代码:

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

这次当我输入“updateName turgut”时,它在控制台屏幕上写下了以下内容:

useEffect {姓名:'Yiğitcan',姓氏:'Uçar',电子邮件:'[email protected]',工作:'计算机工程师',城市:'伊斯坦布尔',...}

newData {姓名:'turgut',姓氏:'Uçar',电子邮件:'[email protected]',工作:'计算机工程师',城市:'伊斯坦布尔',...}

userData {姓名:'Yiğitcan',姓氏:'Uçar',电子邮件:'[email protected]',工作:'计算机工程师',城市:'伊斯坦布尔',…}

useEffect {姓名:'turgut',姓氏:'Uçar',电子邮件:'[email protected]',工作:'计算机工程师',城市:'伊斯坦布尔',…}

所以看起来它已经改变了,但由于某种原因旧的仍然出现在函数中。 当我使用这个函数检索数据运行时,它会打印旧数据:

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);
    }
  }
reactjs react-hooks async-await closures
1个回答
0
投票

评论中提供的链接@David 应该可以回答您的问题。我会尝试添加可能的解决方案:

  1. startAnimation
    提供新数据作为参数:
    function startAnimation(term, step, newUser)
  2. 在依赖于
    startAnimation
    useEffect
    中调用
    userData
    ,这可能看起来有点像这样:
  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]);
© www.soinside.com 2019 - 2024. All rights reserved.