问题描述
我刚开始反应。
现在尝试使用 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-render
而 useState()
是。这种方法被称为使用输入字段的 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 不会在您的代码中发生。