У меня есть ситуация, когда я реализую собственный раскрывающийся фильтр для таблицы в ответ. У меня есть набор выпадающих значений для каждого столбца, и есть кнопка «Применить».
Для этого я сохранил дочерний компонент, который принимает выпадающие значения и отправляет выбранный обратно родительскому.
Фильтрация происходит, но когда я снова открываю это раскрывающееся меню, значения флажков теряются.
Может кто-нибудь сказать, где я ошибаюсь?
я застрял здесь
Песочница: https://codesandbox.io/s/nervous-elgamal-0zztb
Я добавил ссылку на песочницу с соответствующими комментариями. Пожалуйста, посмотрите. Я немного новичок, чтобы реагировать.
Помощь будет очень признательна
import * as React from "react";
import { render } from "react-dom";
import ReactTable from "react-table";
import "./styles.css";
import "react-table/react-table.css";
import Child from "./Child";
interface IState {
data: {}[];
columns: {}[];
selectedValues: {};
optionsForColumns: {};
}
interface IProps {}
export default class App extends React.Component<IProps, IState> {
// Here I have hardcoded the values, but data and optionsForColumns comes from the backend and it is set inside componentDidMount
constructor(props: any) {
super(props);
this.state = {
data: [
{ firstName: "Jack", status: "Submitted", age: "14" },
{ firstName: "Simon", status: "Pending", age: "15" }
],
selectedValues: {},
columns: [],
optionsForColumns: {
firstName: [{ Jack: "4" }, { Simon: "5" }],
status: [{ Submitted: "5" }, { Pending: "7" }]
}
};
}
// Get the values for checkboxes that will be sent to child
getValuesFromKey = (key: any) => {
let data: any = this.state.optionsForColumns[key];
let result = data.map((value: any) => {
let keys = Object.keys(value);
return {
field: keys[0],
checked: false
};
});
return result;
};
// Get the consolidated values from child and then pass it for server side filtering
handleFilter = (fieldName: any, selectedValue: any, modifiedObj: any) =>
{
this.setState(
{
selectedValues: {
...this.state.selectedValues,
[fieldName]: selectedValue
}
},
() => this.handleColumnFilter(this.state.selectedValues)
);
};
// Function that will make server call based on the checked values from child
handleColumnFilter = (values: any) => {
// server side code for filtering
// After this checkbox content is lost
};
// Function where I configure the columns array for the table . (Also data and column fiter values will be set here, in this case I have hardcoded inside constructor)
componentDidMount() {
let columns = [
{
Header: () => (
<div>
<div>
<Child
key = "firstName"
name = "firstName"
options = {this.getValuesFromKey("firstName")}
handleFilter = {this.handleFilter}
/>
</div>
<span>First Name</span>
</div>
),
accessor: "firstName"
},
{
Header: () => (
<div>
<div>
<Child
key = "status"
name = "status"
options = {this.getValuesFromKey("status")}
handleFilter = {this.handleFilter}
/>
</div>
<span>Status</span>
</div>
),
accessor: "status",
},
{
Header: "Age",
accessor: "age"
}
];
this.setState({ columns });
}
//Rendering the data table
render() {
const { data, columns } = this.state;
return (
<div>
<ReactTable
data = {data}
columns = {columns}
/>
</div>
);
}
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
import * as React from "react";
import { Button, Checkbox, Icon } from "semantic-ui-react";
interface IProps {
options: any;
name: string;
handleFilter(val1: any, val2: any, val3: void): void;
}
interface IState {
showList: boolean;
selected: [];
checkboxOptions: any;
}
export default class Child extends React.Component<IProps, IState> {
constructor(props: any) {
super(props);
this.state = {
selected: [],
showList: false,
checkboxOptions: this.props.options.map((option: any) => option.checked)
};
}
// Checkbox change handler
handleValueChange = (event: React.FormEvent<HTMLInputElement>, data: any) => {
const i = this.props.options.findIndex(
(item: any) => item.field === data.name
);
const optionsArr = this.state.checkboxOptions.map(
(prevState: any, si: any) => (si === i ? !prevState : prevState)
);
this.setState({ checkboxOptions: optionsArr });
};
//Passing the checked values back to parent
passSelectionToParent = (event: any) => {
event.preventDefault();
const result = this.props.options.map((item: any, i: any) =>
Object.assign({}, item, {
checked: this.state.checkboxOptions[i]
})
);
const selected = result
.filter((res: any) => res.checked)
.map((ele: any) => ele.field);
console.info(selected);
this.props.handleFilter(this.props.name, selected, result);
};
//Show/Hide filter
toggleList = () => {
this.setState(prevState => ({ showList: !prevState.showList }));
};
//Rendering the checkboxes based on the local state, but still it gets lost after filtering happens
render() {
let { showList } = this.state;
let visibleFlag: string;
if (showList === true) visibleFlag = "visible";
else visibleFlag = "";
return (
<div>
<div style = {{ position: "absolute" }}>
<div
className = {"ui scrolling dropdown column-settings " + visibleFlag}
>
<Icon className = "filter" onClick = {this.toggleList} />
<div className = {"menu transition " + visibleFlag}>
<div className = "menu-item-holder">
{this.props.options.map((item: any, i: number) => (
<div className = "menu-item" key = {i}>
<Checkbox
name = {item.field}
onChange = {this.handleValueChange}
label = {item.field}
checked = {this.state.checkboxOptions[i]}
/>
</div>
))}
</div>
<div className = "menu-btn-holder">
<Button size = "small" onClick = {this.passSelectionToParent}>
Apply
</Button>
</div>
</div>
</div>
</div>
</div>
);
}
}
@Abinthaha обновит песочницу
@Abinthaha В вопрос добавлена обновленная ссылка на песочницу. Переключение не работает, но это нормально. Теперь при щелчке соответствующего раскрывающегося списка «Применить» появляется внутренний API, который обновляет данные. И теперь, если я снова открою это раскрывающееся меню, значения флажков будут потеряны! Также я хочу сохранить значения всех выпадающих значений, чтобы фильтр по столбцам работал правильно.
@Abinthaha Теперь также исправлено переключение. Не могли бы вы найти проблему?
Привет, я все еще не могу просмотреть раскрывающийся список.
@Abinthaha Пожалуйста, найдите ссылку codeandbox codeandbox.io/s/нервный-elgamal-0zztb. Нажмите на значок фильтра
Извините, мой ответ здесь ниже был неправильным.
В вашем основном компоненте вы устанавливаете свои столбцы state.columns только один раз, когда компонент монтируется. Это никогда не изменится после.
Но каждый раз, когда ваши state.data изменяются, ваш основной компонент перерисовывается, а компонент ReactTable
также перерисовывается с новыми данными А ТАКЖЕ с исходными столбцами state.columns, которые не получают информации о выбранных параметрах.
Я думаю, вы должны реконструировать свой объект столбца после каждого обновления, передавая в свои дочерние компоненты реквизит, например selectedOption
.
Ваши дочерние реквизиты зависят от ваших state.selectedValues и state.data. Вы можете реконструировать свой объект столбца в вашем render(). Я использую метод buildColumns, чтобы получить детали из рендеринга.
Вам не нужно свойство columns
в вашем состоянии.
...
constructor(props) {
super(props);
this.state = {
data: [
{ firstName: "Jack", status: "Submitted", age: "14" },
{ firstName: "Simon", status: "Pending", age: "15" },
{ firstName: "Pete", status: "Approved", age: "16" },
{ firstName: "Lucas", status: "Rejected", age: "19" }
],
selectedValues: {},
};
}
...
buildColumns = ({ data, selectedValues = {} }) => {
const { firstName, status } = selectedValues
return [
{
Header: () => (
<div>
<div style = {{ position: "absolute", marginLeft: "10px" }}>
<Child
key = "firstName"
name = "firstName"
options = {this.getValuesFromKey(data, "firstName")}
selectedOption = {firstName}
handleFilter = {this.handleFilter}
/>
</div>
<span>First Name</span>
</div>
),
accessor: "firstName",
sortable: false,
show: true,
displayValue: " First Name"
},
{
Header: () => (
<div>
<div style = {{ position: "absolute", marginLeft: "10px" }}>
<Child
key = "status"
name = "status"
options = {this.getValuesFromKey(data, "status")}
selectedOption = {status}
handleFilter = {this.handleFilter}
/>
</div>
<span>Status</span>
</div>
),
accessor: "status",
sortable: false
},
{
Header: "Age",
accessor: "age"
}
];
}
render() {
const { data, selectedValues } = this.state;
const columns = this.buildColumns({ data, selectedValues })
return (
<div>
<ReactTable
data = {data}
columns = {columns}
defaultPageSize = {10}
className = "-striped -highlight"
/>
</div>
);
}
Вам все еще нужно использовать реквизиты selectedOption
внутри вашего дочернего компонента, чтобы предварительно установить флажок.
старый (и неправильный) ответ:
Когда вы получаете отфильтрованные данные и устанавливаете с ними свое состояние, теперь, когда ваши state.data изменились, он повторно отрисовывает дочерний компонент, который получает реквизиты, полученные от него.
Это повторно инициализирует состояние вашего дочернего компонента до его значения {selected: []}
.
Чтобы предотвратить это, вы должны сделать свой дочерний компонент «управляемым компонентом», сохранив свое состояние только в своем основном компоненте и передав информацию о выбранной опции реквизитами: https://reactjs.org/docs/forms.html#управляемые-компоненты
Спасибо за ответ, можно ли отредактировать фрагмент, пожалуйста? Я немного новичок, чтобы реагировать
Спасибо еще раз! Что значит реконструировать? Если вы имели в виду реконструкцию всего объекта столбца, это снова усложнило бы задачу. Не могли бы вы показать мне фрагмент кода или отредактировать существующую песочницу? Я не понимаю вашу точку зрения, определенно отмечу это как правильный ответ, если он удовлетворяет
Я снова отредактировал свой ответ, надеюсь, так будет понятнее.
Но пока безуспешно
Я не вижу ничего в этой ссылке, связанной с тем, что я предложил. Вы по-прежнему создаете свой объект columns
в ComponentDidMount
, поэтому это делается только один раз и не обновляется. Вы пробовали то, что я вам предложил?
Да . Логика усложнялась и усложнялась. А также selectedOption = {firstName} не будет решать, поскольку в каждом столбце есть набор параметров для выбора. Здесь это не будет отображаться правильно
В песочнице таблица и ничего не рендерится. Кроме того, не отображается ChildComponent как раскрывающийся список. Кажется, что они выстроены в линию. Можете ли вы объяснить проблему здесь? Это действительно выпадающее меню? И вы снова теряете ценность при открытии раскрывающихся списков?