#reactjs
#reactjs
Вопрос:
Я пишу простую страницу реакции, которая отображает 2 разные HTML-таблицы на основе того, какая кнопка нажата на экране. Проблема, с которой я сталкиваюсь, заключается в том, что таблица, которая отображается при каждом нажатии кнопки, связана с предыдущим нажатием кнопки. (Например, если я нажму кнопку 1 один раз, а затем нажму кнопку 2, отобразится таблица, связанная с кнопкой 1.)
Я новичок в react, поэтому, чтобы обновить таблицы, я переработал свой код, чтобы сохранить как можно больше состояния в App.js класс, я создал обратный вызов toggleState, чтобы связать нажатия кнопок с изменением состояния родительского элемента, а затем я передаю это в dataProvider через свойство endpoint. Я понимаю, что, вероятно, именно здесь происходит отключение состояния / пользовательского интерфейса, но я не уверен в причине, поскольку я придерживаюсь принципов react в меру своих возможностей.
моя структура класса выглядит следующим образом:
App
/
/
/
DataProvider ButtonToggle
|
Table
Если это важно, класс table создает таблицу на основе вызова API, я добавлю код для этого, но это не вызывает у меня проблем, поэтому я не считаю, что это источник проблемы.
App.js
import React, { Component } from "react";
import PropTypes from "prop-types";
import DataProvider from "./DataProvider";
import Table from "./Table";
import ButtonToggle from "./ButtonToggle";
class App extends Component {
constructor(props){
super(props);
this.state = {
input : 'employees',
endpoint : "api/employees/"
};
console.log("constructor app: " this.state.input "n" this.state.endpoint);
}
toggleState(input) {
if(input == "employees") {
this.setState({input : input, endpoint: "api/employees/"});
}
else {
this.setState({input : input, endpoint: "api/categories/"});
}
console.log("toggleState " this.state.input "n" this.state.endpoint);
}
render() {
return (
<div className="col-lg-12 grid-margin">
<div className="card">
<div className="card-title">
<div className="row align-items-center justify-content-center">
<div className="col-3"></div>
<div className="col-6">
<h1> Striped Table</h1>
</div>
<div className="col-3"></div>
</div>
<ButtonToggle toggleInput={ (input) => this.toggleState(input)}/>
</div>
<div className="card">
<div className="card-title"></div>
<div className="card-body">
<DataProvider endpoint={this.state.endpoint}
render={data => <Table data={data} />} />
</div>
</div>
</div>
</div>
);
}
}
export default App;
DataProvider.js
class DataProvider extends Component {
static propTypes = {
endpoint: PropTypes.string.isRequired,
render: PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.state = {
data: [],
loaded: false,
placeholder: "Loading..."
};
}
componentWillReceiveProps(props) {
console.log("dataprov: " this.props.endpoint);
this.componentDidMount();
}
componentDidMount() {
fetch(this.props.endpoint)
.then(response => {
if (response.status !== 200) {
return this.setState({ placeholder: "Something went wrong" });
}
return response.json();
})
.then(data => this.setState({ data: data, loaded: true }));
}
render() {
const { data, loaded, placeholder } = this.state;
return loaded ? this.props.render(data) : <p>{placeholder}</p>;
}
}
export default DataProvider;
ButtonToggle.js
class ButtonToggle extends Component {
constructor (props) {
super(props);
}
render() {
return (
<div className="row align-items-center justify-content-center">
<div className="col-3 center-in-div">
<button type="button" className="btn btn-info btn-fw" onClick={this.props.toggleInput.bind(this, 'categories')}> Categories </button>
</div>
<div className="col-3 center-in-div">
<button type="button" className="btn btn-info btn-fw" onClick={this.props.toggleInput.bind(this, 'employees')}>
Employees
</button>
</div>
<div className="col-6"></div>
</div>
);
}
}
export default ButtonToggle;
Table.js : Я не думаю, что это проблема, но я могу быть исправлен.
import React from "react";
import PropTypes from "prop-types";
import key from "weak-key";
const Table = ({ data }) =>
!data.length ? (
<p>Nothing to show. Records: {data.length} </p>
) : (
<div className="table-responsive">
<h2 className="subtitle">
Showing <strong>{data.length} items</strong>
</h2>
<table className="table table-hover">
<thead>
<tr>
{Object.entries(data[0]).map(el => <th key={key(el)}>{el[0]}</th>)}
</tr>
</thead>
<tbody>
{data.map(el => (
<tr key={el.id}>
{Object.entries(el).map(el => <td key={key(el)}>{el[1]}</td>)}
</tr>
))}
</tbody>
</table>
</div>
);
Table.propTypes = {
data: PropTypes.array.isRequired
};
export default Table;
Комментарии:
1. Первым шагом было бы прекратить привязку функции к ButtonToggle — поскольку вы обращаетесь
this
внутрь функции, вы хотите, чтобы она была привязана к родительской. Вы можете легко привязать его к родительскому, объявив его как функцию со стрелкой,toggleState = input => {
, затем вызвать его изнутри ButtonToggle с помощьюonClick={() => this.props.inputToggle("categories")}
etc
Ответ №1:
Ниже приведен минимальный рабочий код, который я смог придумать. Ваши компоненты Button и Table могут быть немыми компонентами, которые будут получать данные из родительского компонента и представлять их. Ваш родительский компонент или контейнер будет иметь логику для установки свойств для кнопки и компонента таблицы. Поскольку компоненты таблицы и кнопки являются немыми, вы можете использовать функциональные компоненты.
Я добавил код для вызова api (я попытался имитировать вызов api) и получения данных в том же родительском компоненте, вы можете разделить их. Вы можете работать над стилем и проверками в соответствии с вашими потребностями. Дайте мне знать, если вам понадобится дополнительная помощь.
class ParentComponent extends Component {
constructor() {
super();
this.state = {
name: "Category"
}
this.onBtnClick = this.onBtnClick.bind(this);
}
componentDidMount() {
this.getData(this.state.name)
}
getData(name) {
if (name === "Category") {
this.apiCall("/Category").then((data) => {
this.setState({ data: data })
})
} else {
this.apiCall("/Employee").then((data) => {
this.setState({ data: data })
})
}
}
apiCall(url) {
return new Promise((res, rej) => {
setTimeout(() => {
if (url === "/Employee") {
res([{ "Emp Name": "AAA", "Emp Age": "20" }, { "Emp Name": "BBB", "Emp Age": "40" }])
} else {
res([{ "Cat Id": "XXX", "Cat Name": "YYY" }, { "Cat Id": "MMM", "Cat Name": "NNN" }])
}
}, 1000)
});
}
onBtnClick(name) {
let newName = "Category"
if (name === newName) {
newName = "Employee"
}
this.setState({ name: newName, data: [] }, () => {
this.getData(newName);
})
}
render() {
return (<>
<ButtonComponent name={this.state.name} onBtnClick={this.onBtnClick}></ButtonComponent>
<TableComponent data={this.state.data} />
</>)
}
}
const ButtonComponent = ({ name, onBtnClick }) => {
return <Button onClick={() => { onBtnClick(name) }}>{name}</Button>
}
const TableComponent = ({ data }) => {
function getTable(data) {
return < table >
<thead>
<tr>
{getHeading(data)}
</tr>
</thead>
<tbody>
{getRows(data)}
</tbody>
</table >
}
function getHeading(data) {
return Object.entries(data[0]).map((key) => {
return <th key={key}>{key[0]}</th>
});
}
function getRows(data) {
return data.map((row, index) => {
return <tr key={"tr" index}>
{Object.entries(data[0]).map((key, index) => {
console.log(row[key[0]]);
return <td key={"td" index}>{row[key[0]]}</td>
})}
</tr>
})
}
return (
data amp;amp; data.length > 0 ?
getTable(data)
: <div>Loading....</div>
)
}