#javascript #reactjs #data-binding
#javascript #reactjs #привязка данных
Вопрос:
Как я могу синхронизировать значение SearchFilter
с FilterGrid
, чтобы я мог фильтровать данные (см. Код ниже)? У меня возникла проблема с очисткой поля от значения при вводе. Должно быть, проблема в том, как я использую перехватчики состояний.
Проблема, вероятно, связана с тем, как я вызываю onChange
внутри handleOnChange
функции, которая находится внутри SearchFilter
. Я передал onChange
«прослушиватель» в качестве свойства фильтра, чтобы я мог получить доступ к значению за пределами фильтра.
Не уверен, что это неправильный способ, потому что привязка значения directy к полю поиска в сетке выдала мне это предупреждение:
«Предупреждение: компонент изменяет контролируемый ввод на неконтролируемый. Вероятно, это вызвано изменением значения с определенного на неопределенное, чего не должно происходить. Выберите между использованием контролируемого или неконтролируемого элемента ввода в течение срока службы компонента. «
Вот ссылка на живую песочницу:
https://codesandbox.io/s/react-material-ui-search-d4073?file=/src/App.jsx
App.jsx
import React, { useEffect, useState } from "react";
import FilterGrid from "./components/FilterGrid";
import jsonData from "./data/data.json";
const fetchData = () => Promise.resolve(jsonData);
const App = () => {
const [data, setData] = useState([]);
useEffect(() => {
fetchData().then((json) => setData(json));
}, []);
const columns = [
{ key: "nameField", field: "name", title: "Name" },
{ key: "ageField", field: "age", title: "Age" }
];
return <FilterGrid data={data} columns={columns} />;
};
export default App;
FilterGrid.jsx
import React, { useState } from "react";
import SearchFilter from "./SearchFilter";
import MaterialTable from "material-table";
const FilterGrid = (props) => {
const { data, columns } = props;
const [filterValue, setFilterValue] = useState("");
const handleChange = (value) => {
setFilterValue(value);
console.log("Value changed...");
};
const applyFilter = (records) =>
records.filter((record) =>
Object.values(record).some((value) => `${value}`.includes(filterValue))
);
const toolbar = () => (
<div
style={{
display: "flex",
flex: 1,
flexDirection: "row",
justifyContent: "flex-end"
}}
>
<SearchFilter
listeners={{
onChange: handleChange
}}
/>
</div>
);
console.log("Changed filter:", filterValue);
return (
<div>
<MaterialTable
data={applyFilter(data)}
columns={columns}
components={{
Toolbar: toolbar
}}
options={{
search: false
}}
/>
</div>
);
};
export default FilterGrid;
SearchFilter.jsx
import { FormControl, Input } from "@material-ui/core";
import React, { useState } from "react";
import PropTypes from "prop-types";
const SearchFilter = (props) => {
const {
listeners: { onChange }
} = props;
const [internalValue, setInternalValue] = useState("");
const handleOnChange = (e) => {
setInternalValue(e.target.value);
onChange(internalValue);
};
return (
<FormControl>
<Input
type="search"
placeholder="Search..."
onChange={(e) => handleOnChange(e)}
value={internalValue}
/>
</FormControl>
);
};
export default SearchFilter;
data.json
[
{ "name": "Bill", "age": 21 },
{ "name": "Bob", "age": 31 },
{ "name": "Brad", "age": 41 }
]
package.json
{
"dependencies": {
"@material-ui/core": "4.11.1",
"material-table": "1.69.2",
"prop-types": "15.7.2",
"react": "17.0.0",
"react-dom": "17.0.0"
}
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>React Material UI Search</title>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/icon?family=Material Icons"
/>
</head>
<body>
<div id="root"></div>
</body>
</html>
Ответ №1:
проблема заключается в асинхронном характере setState при выполнении:
const handleOnChange = (e) => {
setInternalValue(e.target.value);
onChange(internalValue);
};
internalValue по-прежнему остается пустым после onChange
выполнения (оно не ожидает setInternalValue) для обновления состояния.
есть и другие вещи, которые следует учитывать. вы дублируете состояние, что не является хорошей практикой. вам лучше сохранить состояние фильтра FilterGrid
и передать его в качестве реквизита.
еще одна вещь, которую я заметил, это то, что каждый тип ввода размывает поле ввода. Этого можно избежать, если объявить всплывающую подсказку снаружи и не передавать в качестве параметра компонента в FilterGrid
FilterGrid
import React, { useState } from "react";
import SearchFilter from "./SearchFilter";
import MaterialTable from "material-table";
const Toolbar = ({ filterValue, setFilterValue }) => (
<div
style={{
display: "flex",
flex: 1,
flexDirection: "row",
justifyContent: "flex-end"
}}
>
<SearchFilter filterValue={filterValue} setFilter={setFilterValue} />
</div>
);
const FilterGrid = (props) => {
const { data, columns } = props;
const [filterValue, setFilterValue] = useState("");
const applyFilter = (records) =>
records.filter((record) =>
Object.values(record).some((value) => `${value}`.includes(filterValue))
);
console.log("Changed filter:", filterValue);
return (
<div>
<Toolbar filterValue={filterValue} setFilterValue={setFilterValue} />
<MaterialTable
data={applyFilter(data)}
columns={columns}
options={{
search: false
}}
/>
</div>
);
};
export default FilterGrid;
Фильтр поиска
import { FormControl, Input } from "@material-ui/core";
import React from "react";
import PropTypes from "prop-types";
const SearchFilter = (props) => {
const { filterValue, setFilter } = props;
const handleOnChange = (e) => {
setFilter(e.target.value);
};
return (
<FormControl>
<Input
type="search"
placeholder="Search..."
onChange={handleOnChange}
value={filterValue}
/>
</FormControl>
);
};
SearchFilter.propTypes = {
filterValue: PropTypes.string.isRequired,
setFilter: PropTypes.func.isRequired
};
export default SearchFilter;
Комментарии:
1. Таким образом, нет способа настроить панель инструментов для сетки с помощью
components.Toolbar = () => (<Toolbar ... />)
?2. Я не в курсе. Желаемый вами подход приводит к повторному отображению на панели инструментов, что приводит к размытию в SeachField для каждого типа пользователя.