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-render
而 useState()
会。这种方法称为 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=] 这样就不会在您的代码中发生重新渲染。
我是新手。
现在尝试使用 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-render
而 useState()
会。这种方法称为 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=] 这样就不会在您的代码中发生重新渲染。