我正在尝试通过去抖来实现产品列表和搜索功能,但是 在 ProductList 组件中执行分派操作后,SearchProducts 组件的状态 search 为空。有人可以建议下面的代码有什么问题吗?
产品列表
import { fetchData } from "../../services/productService";
import { useDispatch, useSelector } from "react-redux";
import {
fetchProductInfo,
} from "../../store/features/productsSlice";
import ToastContext from "../../context/ToastContext";
import "bootstrap-icons/font/bootstrap-icons.css";
import { useNavigate } from "react-router-dom";
import { FETCH_PRODUCTS_END_URL } from "../../utils/constant";
import SearchProducts from "./SearchProducts";
import SearchBox from "./SearchBox";
const ProductList = () => {
const navigate = useNavigate();
const toastCtx = useContext(ToastContext);
const dispatch = useDispatch();
const { products, loading, error, status } = useSelector(
(state) => state.products
);
const [keySearch, SearchForm] = useForm('');
useEffect(() => {
dispatch(fetchProducts());
}, []);
if (loading) {
return <h2>Products is loading ....</h2>;
}
if (error) {
return <h2>{error}</h2>;
}
return (
<div className="container mt-2">
<h1 className="text-center mt-4">Products List</h1>
<SearchProducts />
<table className="table mt-4">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Category</th>
<th scope="col">Price</th>
</tr>
</thead>
<tbody>
{products.length > 0 &&
products.map((product) => {
return (
<tr key={product._id}>
<td className="col">{product.name}</td>
<td className="col">{product.category}</td>
<td className="col">{product.price}</td>
</tr>
);
})}
{products.length === 0 && (
<tr>
<td className="text-center" colSpan="4">
<strong>No products available !!</strong>
</td>
</tr>
)}
</tbody>
</table>
</div>
);
};
export default ProductList;
搜索产品
import { useDispatch } from "react-redux";
import { searchProducts } from "../../store/features/productsSlice";
const SearchProducts = (props) => {
const [search, setSearch] = useState("");
const dispatch = useDispatch();
let seacrhClick = useCallback(() => {
if(search.length > 0){
dispatch(searchProducts(search));
}
}, [search]);
useEffect(() => {
let timerID = setTimeout(() => {
seacrhClick();
}, 1000);
return () => {
clearTimeout(timerID);
};
}, [search]);
return (
<div className="container">
<input
key={`search_${search}`}
className="w-50 form-control"
type="text"
name="search"
autoFocus
value={search || ''}
placeholder="Search Products"
onChange={(e) => {
setSearch(e.target.value);
}}
/>
</div>
);
};
export default SearchProducts;
redux 中的 searchProduct 功能
export const searchProducts = (key) => {
return async (dispatch,getState) => {
try {
dispatch(setStatus(PRODUCTS_STATUS.REQUESTED));
dispatch(setLoading(true));
const response = await fetchData(`${PRODUCTS_SEARCH_URL}/${key}`);
dispatch(setStatus(PRODUCTS_STATUS.SUCCESS));
dispatch(setLoading(false));
dispatch(setProducts(response?.data));
} catch (err) {
dispatch(
setError(
err?.response?.data?.message
? err?.response?.data?.message
: err.message
)
);
dispatch(setLoading(false));
dispatch(setStatus(PRODUCTS_STATUS.FAILED));
}
}
}
当您主动搜索时,即调度
searchProducts
操作时,ProductList
组件有条件地呈现一些替代 UI。这意味着 SearchProducts
已卸载。当搜索后 loading
和 error
状态再次为 false 时,SearchProducts
会重新挂载,再次使用默认初始状态。
重构渲染的 UI 以保持
SearchProducts
的安装。
重构示例:
const ProductList = () => {
const dispatch = useDispatch();
const { products, loading, error, status } = useSelector(
(state) => state.products
);
useEffect(() => {
dispatch(fetchProducts());
}, []);
return (
<div className="container mt-2">
<h1 className="text-center mt-4">Products List</h1>
<SearchProducts />
{loading
? <h2>Products is loading ....</h2>
: (
<>
{error && <h2>{error}</h2>}
<table className="table mt-4">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Category</th>
<th scope="col">Price</th>
</tr>
</thead>
<tbody>
{products.length
? products.map((product) => (
<tr key={product._id}>
<td className="col">{product.name}</td>
<td className="col">{product.category}</td>
<td className="col">{product.price}</td>
</tr>
))
: (
<tr>
<td className="text-center" colSpan="4">
<strong>No products available !!</strong>
</td>
</tr>
)
}
</tbody>
</table>
</>
)
}
</div>
);
};