React useState 挂钩 - 我想在仅按下提交按钮而不是 onChange 时呈现某些组件

React useState hook - I want to render certain component when only press submit button not when onChange

我是新手。

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

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

但是不知道如何只在按下提交按钮时渲染。

我的最终目标是在输入名称并按回车键时,如果值不包含在 api 中,setShake make shake True 如果为 True,将 shake-cloud class 放入 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("");

然后从 input 中删除 onChange={updateSearch}value={search} 并使用 属性 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 输入29=] 这样就不会在您的代码中发生重新渲染。