React useState hook - 我想在只按提交按钮而不是在 onChange 时呈现某些组件

问题描述

我刚开始反应。

现在尝试使用 React 钩子制作表单,我只想在按下提交按钮时渲染 Cloud 组件。但它呈现了每个调用的 onChange。

我知道 onChange 重新渲染的原因也是 useState 钩子。

但不知道如何仅在按下提交按钮时呈现。

我的最终目标是在写入名称并按回车键时,如果 api 中不包含值,则 setShake 使 Shake 为 True,如果为 True,则将 Shake-cloud 类放在 Cloud.js 中。

反应太难了:(

谢谢你的帮助 :)

App.js

import React,{ useState,useEffect } from "react";
import "./App.css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSearch } from "@fortawesome/free-solid-svg-icons";
import "./search.css";
import PageTitle from "./component/PageTitle";
import Cloud from "./component/Cloud";
import Loading from "./component/Loading";

//https://api.color.pizza/v1/
//data.colors[0].name

const App = () => {
    const [isLoading,setIsLoading] = useState(false);
    const [colorNames,setColorNames] = useState("");
    const [search,setSearch] = useState("");
    const [query,setQuery] = useState("");
    const [cloudHex,setCloudHex] = useState("ivory");
    const [shake,setShake] = useState(false);

    useEffect(() => {
        getColorLists();
    },[]);

    const getColorLists = async () => {
        const res = await fetch(`https://api.color.pizza/v1/`);
        const data = await res.json();
        await setColorNames(data);
        setIsLoading(true);
    };

    const isColor = () => {
        let makeUpper =
            query.search(/\s/) == -1
                ? query.charat(0).toupperCase() + query.slice(1)
                : query
                      .split(" ")
                      .map((i) => i.charat(0).toupperCase() + i.slice(1))
                      .join(" ");

        for (let i = 0; i < colorNames.colors.length; i++) {
            if (colorNames.colors[i].name == makeUpper) {
                setCloudHex(colorNames.colors[i].hex);
                return;
            } else if (i == colorNames.colors.length - 1) {
                return makeShake();
            }
        }
    };

    const updateSearch = (e) => {
        setSearch(e.target.value);
    };
    const getSearch = (e) => {
        e.preventDefault();
        setQuery(search);
        isColor();
    };

    const makeShake = async () => {
        await setShake(true)
        await setShake(false)
    }

    return (
        <>
            {!isLoading ? (
                <Loading />
            ) : (
                <div className="App">
                    <div className="app-wrap">
                        <PageTitle />
                        <div className="search-wrap">
                            <form onSubmit={getSearch} className="search-form">
                                <input
                                    className="search-bar"
                                    type="text"
                                    value={search}
                                    onChange={updateSearch}
                                />
                                <button type="submit" className="search-button">
                                    <FontAwesomeIcon
                                        icon={faSearch}
                                        className="search"
                                    />
                                </button>
                            </form>
                        </div>
                        <Cloud cloudhex={cloudHex} shake={shake} />
                    </div>
                </div>
            )}
        </>
    );
};

export default App;

Cloud.js

import React,{useEffect} from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCloud } from "@fortawesome/free-solid-svg-icons";
import './cloud.css';

const Cloud = ({cloudhex,shake}) => {

    useEffect(() => {
        
    },[])

    console.log(shake)
    return (
        <div className={`cloud-wrap ${ shake ? "shake-cloud":''}`}>
            <span className="cloudhexname">{cloudhex}</span>
            <FontAwesomeIcon icon={faCloud} className="cloud" style={{color:`${cloudhex}`}} />
        </div>
    );
};

export default Cloud;

解决方法

就那样做

如果你想在表单提交后渲染云组件,那么设置一个标志并切换它,这里我采用 clicked 状态

import React,{ useState,useEffect } from "react";
import "./App.css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSearch } from "@fortawesome/free-solid-svg-icons";
import "./search.css";
import PageTitle from "./component/PageTitle";
import Cloud from "./component/Cloud";
import Loading from "./component/Loading";

//https://api.color.pizza/v1/
//data.colors[0].name

const App = () => {
    const [isLoading,setIsLoading] = useState(false);
    const [colorNames,setColorNames] = useState("");
    const [search,setSearch] = useState("");
    const [query,setQuery] = useState("");
    const [cloudHex,setCloudHex] = useState("ivory");
    const [shake,setShake] = useState(false);
    const [clicked,setClicked] = useState(false);

    useEffect(() => {
        getColorLists();
    },[]);

    const getColorLists = async () => {
        const res = await fetch(`https://api.color.pizza/v1/`);
        const data = await res.json();
        await setColorNames(data);
        setIsLoading(true);
    };

    const isColor = () => {
        let makeUpper =
            query.search(/\s/) == -1
                ? query.charAt(0).toUpperCase() + query.slice(1)
                : query
                      .split(" ")
                      .map((i) => i.charAt(0).toUpperCase() + i.slice(1))
                      .join(" ");

        for (let i = 0; i < colorNames.colors.length; i++) {
            if (colorNames.colors[i].name == makeUpper) {
                setCloudHex(colorNames.colors[i].hex);
                return;
            } else if (i == colorNames.colors.length - 1) {
                return makeShake();
            }
        }
    };

    const updateSearch = (e) => {
        setSearch(e.target.value);
    };
    const getSearch = (e) => {
        e.preventDefault();
        setClicked(true);
        setQuery(search);
        isColor();
    };

    const makeShake = async () => {
        await setShake(true)
        await setShake(false)
    }

    return (
        <>
            {!isLoading ? (
                <Loading />
            ) : (
                <div className="App">
                    <div className="app-wrap">
                        <PageTitle />
                        <div className="search-wrap">
                            <form onSubmit={getSearch} className="search-form">
                                <input
                                    className="search-bar"
                                    type="text"
                                    value={search}
                                    onChange={updateSearch}
                                />
                                <button type="submit" className="search-button">
                                    <FontAwesomeIcon
                                        icon={faSearch}
                                        className="search"
                                    />
                                </button>
                            </form>
                        </div>
                        {clicked && <Cloud cloudhex={cloudHex} shake={shake} />}
                    </div>
                </div>
            )}
        </>
    );
};

export default App;
,

在这种情况下,一个好的方法是使用 useRef() Hook 来存储我们的搜索字段值,而不是使用 useState()。因为 useRef() Hook does not force a re-renderuseState() 是。这种方法被称为使用输入字段的 un-controlled 方法。

您基本上需要对代码进行如下修改:

const search = useRef("");

然后从 onChange={updateSearch} 中删除 value={search}input 并使用属性 ref={search}。使您的输入如下所示:

<input 
    className="search-bar"
    type="text"
    ref={search}
/>

然后在提交处理程序中,您可以使用 search.current.value 获取输入字段的值。所以你的 getSearch() 看起来像

const getSearch = (e) => {
    e.preventDefault();
    setClicked(true);
    setQuery(search.current.value);
    isColor();
};

假设用户输入了一个输入。如果没有,那么您可以在 getSearch() 表单提交处理程序中使用 setQuery() 之前设置验证。

if(search.current.value){
   setQuery();
}

注意: 如果您的项目中有任何其他 controlled inputs,您可以使用 un-controlled 将 then 更改为 refs 输入,这样重新-renders 不会在您的代码中发生。