我的导航组件上有一个搜索栏。 每次击键后,输入都会失去焦点,您必须重新单击它才能键入下一个键。
这是输入:
<input
type="text"
name="search"
placeholder="Search"
value={search}
onChange={handleInputChange}
/>
下面是句柄输入更改函数:
function handleInputChange(event) {
event.preventDefault();
let value = event.target.value;
setSearch(value);
}
这是设置搜索的钩子:
const [search, setSearch] = useState("");
我尝试在输入中添加一个键,但这不起作用。 当我将搜索输入移动到新组件时,这也不起作用。
以下是完整的代码:
import React, { useEffect, useState, useCallback } from "react";
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import { Row, Col } from '../Grid';
import IconButton from '@material-ui/core/IconButton';
import SearchIcon from '@material-ui/icons/Search';
import ShoppingCartOutlinedIcon from '@material-ui/icons/ShoppingCartOutlined';
import MenuIcon from '@material-ui/icons/Menu';
import Badge from '@material-ui/core/Badge';
import useScrollTrigger from '@material-ui/core/useScrollTrigger';
import Slide from '@material-ui/core/Slide';
import SideMenu from '../SideMenu';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { css } from 'glamor';
import "./style.css";
function Navbar(props) {
const cart = useSelector(state => state.cart);
const [cartTotal, setCartTotal] = useState(0);
const [loggedIn, setLoggedIn] = useState(false);
const [search, setSearch] = useState("");
const [isOpen, setIsOpen] = useState(false);
const [renderMiddleCol, setMiddleCol] = useState(true);
const [menuClass, setMenuClass] = useState("no-menu");
useEffect(() => {
if (cart[0]) {
setCartTotal(cart[0].line_items.length)
}
}, [cart[0]]);
useEffect(() => {
if (window.sessionStorage.id) {
setLoggedIn(true);
} else {
setLoggedIn(false);
}
}, [loggedIn]);
useEffect(() => {
if (window.innerWidth < 450) {
setMiddleCol(false);
} else {
setMiddleCol(true);
}
}, [window.innerWidth]);
function HideOnScroll(props) {
const { children, window } = props;
const trigger = useScrollTrigger({ target: window ? window() : undefined });
return (
<Slide appear={false} direction="down" in={!trigger}>
{children}
</Slide>
);
}
HideOnScroll.propTypes = {
children: PropTypes.element.isRequired,
window: PropTypes.func,
};
function CheckCart() {
if (window.sessionStorage.id) {
window.location.href = "/cart";
} else {
toast("Please login to view your cart", {
className: css({
background: '#3E0768',
boxShadow: '2px 2px 20px 2px rgba(0,0,0,0.3)',
borderRadius: '17px'
}),
bodyClassName: css({
fontSize: '20px',
color: 'white'
}),
progressClassName: css({
background: "linear-gradient(90deg, rgba(0,0,0,1) 0%, rgba(62,7,104,1) 80%)"
})
});
}
}
function Search() {
if (search) {
sessionStorage.setItem("search", search);
window.location.href = "/search";
} else {
toast("Search field cannot be empty", {
className: css({
background: '#3E0768',
boxShadow: '2px 2px 20px 2px rgba(0,0,0,0.3)',
borderRadius: '17px'
}),
bodyClassName: css({
fontSize: '20px',
color: 'white'
}),
progressClassName: css({
background: "linear-gradient(90deg, rgba(0,0,0,1) 0%, rgba(62,7,104,1) 80%)"
})
});
}
}
function logOut(event) {
event.preventDefault();
setIsOpen(false);
sessionStorage.clear();
window.location.href = "/login";
}
function handleInputChange(event) {
event.preventDefault();
let value = event.target.value;
setSearch(value);
}
function toggleMenu(event) {
event.preventDefault();
setIsOpen(!isOpen);
if (menuClass === "no-menu") {
setMenuClass("menu-background");
} else {
setMenuClass("no-menu");
}
}
const theme = createMuiTheme({
palette: {
primary: {
main: '#000000',
contrastText: '#ffffff',
},
secondary: {
light: '#3E0768',
main: '#3E0768',
contrastText: '#ffffff',
},
tertiary: {
main: '#ffffff',
}
},
});
return (
<MuiThemeProvider theme={theme}>
<HideOnScroll {...props}>
<AppBar position="fixed" color="primary">
<Toolbar>
<Col size="md-1">
<IconButton
onClick={toggleMenu}
aria-label="Menu"
>
<MenuIcon
fontSize="large"
className="white"
/>
</IconButton>
</Col>
<Col size="md-2">
<a href="/" className="white "><h6>Demo Company</h6></a>
</Col>
{renderMiddleCol ? (
<Col size="lg-6 md-5 sm-3" />
) : (<div />)}
<Col size="md-2 4">
<Row no-gutters>
<div className="search-box">
<Col size="md-2 1">
<IconButton onClick={Search} aria-label="search" >
<SearchIcon className="white" />
</IconButton>
</Col>
<Col size="md-8 9">
{/* <SearchForm
value={search}
onChange={handleInputChange}
/> */}
<input
className="search-field white"
type="text"
name="search"
placeholder="Search"
value={search}
onChange={handleInputChange}
/>
</Col>
</div>
</Row>
</Col>
<Col size="md-1">
<IconButton
onClick={CheckCart}
aria-label="Go to cart"
>
<MuiThemeProvider theme={theme}>
<Badge
badgeContent={cartTotal}
color="secondary"
>
<ShoppingCartOutlinedIcon className="white" />
</Badge>
</MuiThemeProvider>
</IconButton>
</Col>
</Toolbar>
</AppBar>
</HideOnScroll>
<SideMenu
isOpen={isOpen}
menuClass={menuClass}
toggleMenu={toggleMenu}
loggedIn={loggedIn}
logOut={logOut}
/>
</MuiThemeProvider>
);
}
export default Navbar;
我最终在输入中使用了一个 ref,并将其设置为每次重新渲染的焦点。 这是修复它的代码。
const [search, setSearch] = useState("");
const searchInput = React.useRef(null);
useEffect(() => {
searchInput.current.focus();
}, [search]);
这是输入:
<input
ref={searchInput}
className="search-field white"
type="text"
name="search"
placeholder="Search"
value={search}
onChange={handleInputChange}
/>
解决方案的功劳:反应:将焦点放在组件DidMount上,如何使用钩子来做到这一点?
以下是我发现有帮助的详细说明: https://reactkungfu.com/2015/09/react-js-loses-input-focus-on-typing/
总结一下:如果没有不变的键,React 会在状态更改时丢弃你受控输入的先前实例,并在其位置创建一个新实例。具有焦点的输入在其值更改后立即删除。
确保:
- 受控输入具有
key
属性 key
的值不会以任何方式从输入的value
派生,因为您不希望在值更改时更改键
为我解决这个问题的是不使用内部组件。如
const NewComponent = (props: {text: string}) => <div>{text}</div>;
return <div><NewComponent text="Text" /><div>;
当我以这种方式声明输入组件时,它会导致每次击键时重新渲染。解决方案是将组件提取到另一个文件中,或者只是将整个组件放在 JSX 中,而不使用上述方法来清理代码。
这与 React 无法知道何时重新渲染有关。
我不知道确切的原因,但是通过更改此代码解决了我的焦点问题:
import { Route } from 'react-router-dom'
<Route path='xxx' component={() => <TheComponent... />}
其中组件包含键入时失去焦点的输入元素,到以下代码:
<Route path='xxx'>
<TheComponent... />
</Route>
看到我的SO问题,希望有人很快就会阐明它是如何工作的