这是我第一次接触Reaction,也是我在Stack上的第一个问题,请耐心等待.

我有一个干净的App.jsx来呈现父组件Lettersoup.

App.jsx

import './assets/CSS/App.css';
import LetterSoup from './components/LetterSoup';

function App() {

    return ( 
        <div className="app_container">
            <LetterSoup />
        </div>);

}
export default App;

这款应用有一个父Lettersoup,它调用controlpanelgridwords组件.与这个问题相关的是controlpanel.我将注释掉gridwords的div,因为它们正在工作,不需要用来解决这个问题.

Lettersoup上有基本的布局和逻辑...

Lettersoup.jsx

import { useEffect, useState, useRef } from 'react';
import ControlPanel from '../components/ControlPanel';
import Grid from '../components/Grid';
import Words from '../components/Words';

function LetterSoup() {

    const [soupRequest, setsoupRequest] = useState(null);
    const [letterSoup, setletterSoup] = useState(null);
    const [isLoading, setisLooading] = useState(true);
    const childToParent = (childdata) => {
        console.log("CLICK");
        setsoupRequest(childdata);
    }
    const contents = isLoading == true
        ? <p>Loading...</p>
        :
        <div className="lettersoup_container">
            <h1 id="tabelLabel">Sopa de letras (PT)</h1>
            <div className="controlpanel_container">
                {soupRequest && <ControlPanel soupRequest={soupRequest} childToParent={childToParent} />}
            </div>
            <div>
                {letterSoup.wordsNum} palavras
            </div>
/*          <div className="grid_container">
                <Grid board={letterSoup.board} />
            </div>
            <div className="words_container">
                <Words words={letterSoup.words} />
            </div>
*/
        </div>;

    useEffect(() => {
        let ignore = false;
        async function fetchData(sr) {
            let aux;
            console.log("REQ " + JSON.stringify(sr));
            if (sr == null) {
                aux = (`/lettersoup/15/10/4`);
                sr = ({ gridNum: 15, wordNum: 10, charNum: 4 });
                setsoupRequest(sr);
                console.log("need new");
            }
            else {
                aux = (`/lettersoup/${sr.gridNum}/${sr.wordNum}/${sr.charNum}`);
                console.log("had one");
            }

            try {
                const response = await fetch(aux);
                if (!ignore)
                    if (response.ok) {
                        const data = await response.json();
                        setletterSoup(data);
                        setisLooading(false);
                    } else throw Error("nao é ok");
            }
            catch {
                console.log(error.message)
            }
        }
        fetchData(soupRequest);

        return () => {
            //cleanup
            ignore = true
            setisLooading(true);
        };
    }, [soupRequest]);

    return (<div>{contents}</div>);
}

export default LetterSoup;

第二个On具有用于更改后续API获取以进行渲染的参数的控件.

Controlpanel.jsx

import { useId, useState } from 'react';

function ControlPanel({ soupRequest, childToParent }) {

    const inputId = useId();
    const [tmpRequest, setmpRequest] = useState(soupRequest);


    return (
        <form className="controlpanel">
            <div className="controlpanel_settings">
                <div className="controlapanel_setting"><label htmlFor={inputId}>Tamanho da grelha</label>
                    <input type="number" value={tmpRequest.gridNum} min="10" max="25" step="5" id={inputId} name="gridNum" onChange={event => setmpRequest({ gridNum: parseInt(event.target.value), wordNum: parseInt(tmpRequest.wordNum), charNum: parseInt(tmpRequest.charNum) })} className="controlpanel_setting_input" />

                </div>
                <div className="controlapanel_setting"><label htmlFor={inputId + 1}>Número de palavras</label>
                    <input type="number" value={tmpRequest.wordNum} min="10" max="50" step="1" id={inputId + 1} name="wordNum" onChange={event => setmpRequest({ gridNum: parseInt(tmpRequest.gridNum), wordNum: parseInt(event.target.value), charNum: parseInt(tmpRequest.charNum) })} className="controlpanel_setting_input" />
                </div>
                <div className="controlapanel_setting"><label htmlFor={inputId + 2}>Mínimo de letras</label>
                    <input type="number" value={tmpRequest.charNum} min="3" max="20" step="1" id={inputId + 2} name="charNum" onChange={event => setmpRequest({ gridNum: parseInt(tmpRequest.gridNum), wordNum: parseInt(tmpRequest.wordNum), charNum: parseInt(event.target.value) })} className="controlpanel_setting_input" />
                </div>
            </div>
            <div className="controlpanel_button">
                <button onClick={() => { childToParent(tmpRequest); }}>GO</button>
            </div>
        </form>
    );
}

export default ControlPanel;

我可以获得从子进程发送的父进程的更新值soupRequest,但一旦*useEffect*运行,soupRequest就被设置为null,从而一遍又一遍地呈现默认值.

我所期望的是从controlpanel组件发送的soupRequest保持不变,从而允许我获取正确的资源.如果它在工作,controlpanel上的输入将保持先前设置的值,而不是缺省值.

我已经阅读和try 了很多东西,包括useRefs个等等,尽管我喜欢学习的旅程,我读了很多,仍然被困住了,我想继续前进,学习其他东西.几乎可以肯定的是,这是你们一 blink 就会笑着解决的问题之一,或者甚至不会经历,但请给我一个推动力.

希望我能提供所需的代码和解释.如果没有,请让我知道,我会改进的.

推荐答案

ControlPanel组件呈现一个form元素,你用来调用回调的button元素也提交表单,由于你没有阻止默认表单操作的发生,页面被重新加载,因此整个应用和LetterSoup都被重新挂载,并带有它们的初始状态值.

如果未指定,则默认情况下,HTMLbutton元素使用type="submit".

或者将该按钮指定为除提交按钮以外的任何按钮:

function ControlPanel({ soupRequest, childToParent }) {
  const inputId = useId();
  const [tmpRequest, setmpRequest] = useState(soupRequest);

  return (
    <form className="controlpanel">
      ...
      <div className="controlpanel_button">
        <button
          type="button"
          onClick={() => childToParent(tmpRequest)}
        >
          GO
        </button>
      </div>
    </form>
  );
}

或者将childToParent调用移动到form元素的onSubmitprops 并阻止表单提交:

function ControlPanel({ soupRequest, childToParent }) {
  const inputId = useId();
  const [tmpRequest, setmpRequest] = useState(soupRequest);

  return (
    <form
      className="controlpanel"
      onSubmit={(e) => {
        e.preventDefault(); // <-- prevent form action
        childToParent(tmpRequest);
      }}
    >
      ...
      <div className="controlpanel_button">
        <button type="submit">
          GO
        </button>
      </div>
    </form>
  );
}

您会希望养成alwaysbutton个元素显式传递typeprops 的习惯.

Javascript相关问答推荐

序列查找器功能应用默认值而不是读取响应

有条件的悲剧

未捕获错误:在注销后重定向到/login页面时找不到匹配的路由

Msgraph用户邀请自定义邮箱模板

TypeScript索引签名模板限制

将旋转的矩形zoom 到覆盖它所需的最小尺寸&S容器

无法访问Vue 3深度监视器中对象数组的特定对象值'

在Three JS中看不到补间不透明度更改效果

我怎么才能得到Kotlin的密文?

当id匹配时对属性值求和并使用JavaScript返回结果

将内容大小设置为剩余可用空间,但如果需要,可在div上显示滚动条

如何在不影响隐式类型的情况下将类型分配给对象?

Use Location位置成员在HashRouter内为空

如何在Bootstrap中减少网格系统中单个div的宽度

在渲染turbo流之后滚动到元素

未捕获的不变违规:即使在使用DndProvider之后也应使用拖放上下文

单击子按钮时将活动切换类添加到父分区

为什么我的InDesign Java脚本不能完全工作?

调试jQuery代码以获取所有行的总和(票证类型)

鼠标进入,每秒将图像大小减小5%