由于react-redux中的无限循环,我很头疼,我不知道是什么原因造成的,请帮忙!!!
我正在尝试提交用户状态(是/否)这是我的表单的样子
这是我的文件的结构 用户文件夹
这是我最终遇到的运行时错误: 超出最大更新深度。当组件在 componentWillUpdate 或 componentDidUpdate 中重复调用 setState 时,可能会发生这种情况。 React 限制嵌套更新的数量以防止无限循环。
SearchForm.js
import React, {Component, useRef} from "react";
import axios from "axios";
import {connect} from 'react-redux';
import {doToggleLoadClients, doUpdateSearchClientsOptions} from '../../apps/actions'
const getUsers = async (searchOption) => {
const {page, userName, active} = searchOption;
const searchParams = `?page=${page}&userName=${userName}&active=${active}`;
return await axios.get(
process.env.REACT_APP_WP_API + process.env.REACT_APP_GET_USERS + searchParams
);
};
class SearchForm extends Component {
constructor(props) {
super(props);
this.state = {
userName: '',
companyName: '',
active: '',
allUsers: [], //{"clients":[{}..{}],"total":123}
userNameSuggestionsList: [],
isDataReady: false
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleClearResults = this.handleClearResults.bind(this);
}
/// -- Common events ----
// update the the table when first load
async componentDidMount() {
const searchOption = {page: 'all'}
const responseData = await getUsers(searchOption);
this.setState({allUsers: responseData.data.clients});
}
handleSubmit = async (event) => {
event.preventDefault();
const searchOptions = this.state;
this.props.onUpdateSearchClientsOptions(searchOptions);
this.props.onToggleLoadClientsFlag();
console.log("Searching for search form:", searchOptions);
};
handleClearResults = () => {
this.setState({
companyName: "",
userName: "",
clientStatus: ""
});
};
handleUserStatusChange = event => {
this.setState({[event.target.name]: event.target.value});
};
render() {
const {
userName,
userNameSuggestionsList
} = this.state;
const userNameInputProps = {
placeholder: 'Search User Name',
value: userName,
onChange: this.onUsernameChange
};
return (
<Form onSubmit={this.handleSubmit}>
<Form.Group as={Row}>
<Form.Label column sm="2" className="text-right label">
User Name
</Form.Label>
<Col sm="9">
<Autosuggest
suggestions={userNameSuggestionsList}
onSuggestionsFetchRequested={this.onUsernameSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onUsernameSuggestionsClearRequested}
getSuggestionValue={this.getSuggestionUserNameValue}
renderSuggestion={this.renderUserNameSuggestion}
inputProps={userNameInputProps}
theme={theme}
/>
</Col>
</Form.Group>
<Form.Group as={Row}>
<Form.Label column sm="2" className="text-right label">
User Status
</Form.Label>
<Col sm="9">
<Form.Control
className="input"
as="select"
name="active"
value={this.state.active}
onChange={this.handleUserStatusChange}>
>
<option>ALL</option>
<option>Y</option>
<option>N</option>
</Form.Control>
</Col>
</Form.Group>
<Row>
<Col sm={2}/>
<Col sm={9}>
<Button type="submit" size="lg" onClick={this.handleSubmit}>
SEARCH
</Button>
</Col>
</Row>
</Form>
);
}
}
const mapStateToProps = state => ({
searchOptions: state.searchClientsOption.searchOptions,
userName: state.session.userName
}
);
const mapDispatchToProps = dispatch => ({
onToggleLoadClientsFlag: () => dispatch(doToggleLoadClients()),
onUpdateSearchClientsOptions: searchOptions => dispatch(doUpdateSearchClientsOptions(searchOptions))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(SearchForm);
Users.js
import React, {Component, useState} from "react";
import {Navigate } from "react-router-dom";
import UsersTable from "./UsersTable";
import SearchForm from "./SearchForm";
// Search users
class Users extends Component {
render() {
if (!this.props.id ) {
return <Navigate to={"/admin/login"} />;
}
return (
<div className="my-account">
<Navbar {...this.props} handleLogin={this.props.handleLogin} />
<Container>
<Row>
<Col md={12} className="mx-auto">
<h2 className="text-monospace font-weight-bold text-left mt-3">
Users
</h2>
<Card className="text-left">
<Card.Header className="card-header-account text-light">
Search
</Card.Header>
<Card.Body>
<SearchForm />
</Card.Body>
</Card>
</Col>
</Row>
<Row>
<Col md={12} className="mx-auto">
<Card className="my-5 text-left">
<Card.Header className="card-header-account text-light">
Results
</Card.Header>
<Card.Body>
<UsersTable handleLogin={this.props.handleLogin} />
</Card.Body>
</Card>
</Col>
</Row>
</Container>
</div>
);
}
}
export default Users;
UsersTable.js
import React, {Component} from "react";
import axios from 'axios';
import {connect} from 'react-redux';
import {doSetOptions} from '../../apps/actions';
import {doToggleLoadClients} from '../../apps/actions';
/**
* Calling api users
* @param searchOption
* @returns {Promise<AxiosResponse<any>>}
*/
const getUsers = async (searchOption) => {
const {userName, active, page, orderBy, descAsc } = searchOption;
const searchParams = `?page=${page}&orderBy=${orderBy}&descAsc=${descAsc}&userName=${userName}&active=${active}`;
return await axios.get(
process.env.REACT_APP_WP_API + process.env.REACT_APP_GET_USERS + searchParams
);
};
/**
*
* @param self
* @returns {Promise<boolean>}
*/
const submitUpdateUser = async (self) => {
const {updateId, active, updateUseName } = self.state;
let userName = updateUseName;
let id = updateId;
const response = await axios.post(
process.env.REACT_APP_WP_API + process.env.REACT_APP_UPDATE_USER,
{
id,
userName,
active
}
);
if (response.data.error) {
this.setState({error: response.data.error.text});
return false;
}
else {
return true;
}
};
class UsersTable extends Component {
constructor(props) {
super(props);
this.state = {
clients:[],
errors: [],
page: 0,
total: 0,
limit: 10,
orderBy: "id",
isDataReady: false,
error: "",
dsc_userName: false,
dsc_active: false,
dsc_id: true,
showModal:false,
updateId: '',
updateUseName:'',
errorSubmit: false,
};
this.handleSort = this.handleSort.bind(this);
this.handleClick = this.handleClick.bind(this);
this.handleNext = this.handleNext.bind(this);
this.handlePrevious = this.handlePrevious.bind(this);
this.updateTable = this.updateTable.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleCloseModal = this.handleCloseModal.bind(this);
this.handleShowModal = this.handleShowModal.bind(this);
this.handleChange = this.handleChange.bind(this);
}
// update the table when submit search form
async updateTable() {
const descAsc = this.state["dsc_" + this.state.orderBy] ?"asc": "desc";
const extraOptions = { page: this.state.page, orderBy: this.state.orderBy, descAsc: descAsc }
const searchOptions = {...this.props.searchOptions, ...extraOptions};
const response = await getUsers( searchOptions);
this.setState({
clients: response.data.clients, total: response.data.total, isDataReady: true
});
}
// update the table when submit search form
componentDidUpdate() {
const { loadNewData } = this.props;
if (loadNewData) {
this.setState({
page: 0
}, () => this.updateTable());
}
}
// update the the table when first load
async componentDidMount() {
this.setState({ isDataReady: false });
const searchOptions = this.props.searchOptions;
const response = await getUsers(searchOptions);
this.setState({
clients: response.data.clients,
total: response.data.total,
isDataReady: true
});
}
handleShowModal(id, userName, active) {
this.setState({
updateId: id,
updateUseName:userName,
active:active,
showModal: true,
errorSubmit: false
});
}
handleChange = event => {
this.setState({ [event.target.name]: event.target.value, errorSubmit: false });
};
handleSubmit = async event => {
event.preventDefault();
const self = this;
//console.log('Update user info submitted:', this.state);
await this.setState({ errors: []});
let success = await submitUpdateUser(self);
if( success ) {
alert('User info already updated!');
await this.updateTable();
}
this.handleCloseModal();
};
render() {
const { clients, isDataReady } = this.state;
const pageCount = Math.ceil(parseInt(this.state.total) / 100);
let errMsg;
errMsg = this.state.errorSubmit && (
<Alert variant = 'danger' >
<ul>
<li>Please select the Customer Status</li>
</ul>
</Alert>
);
const header = (
<thead>
<tr>
<th>User ID
<SortButton
id="id"
handleSort={this.handleSort}
btnSort={this.state.dsc_id ? "fas fa-caret-down" : "fas fa-caret-up"}
/>
</th>
<th>User Name
<SortButton
id="id"
handleSort={this.handleSort}
btnSort={this.state.dsc_userName ? "fas fa-caret-down" : "fas fa-caret-up"}
/>
</th>
<th>Company</th>
<th>Email</th>
<th>Phone</th>
<th>Status
<SortButton
id="active"
handleSort={this.handleSort}
btnSort={this.state.dsc_active ? "fas fa-caret-down" : "fas fa-caret-up"}
/>
</th>
</tr>
</thead>
);
if (!isDataReady) {
return (
<Table striped bordered hover>
{header}
<tbody>
<tr>
<td colSpan="8">Loading ...</td>
</tr>
</tbody>
</Table>
);
}
if (clients.length === 0) {
return (
<div className="OrdersTable">
No records found
</div>
)
}
return (
<div className="OrdersTable">
<Footer
page={this.state.page}
total={this.state.total}
offset={100}
handleClick={this.handleClick}
handleNext={this.handleNext}
handlePrevious={this.handlePrevious}
pageCount={pageCount}
/>
<Modal show={this.state.showModal} onHide={this.handleCloseModal} size="lg">
<Modal.Header closeButton>
<Modal.Title>Update Customer Info</Modal.Title>
</Modal.Header>
<Form onSubmit={this.handleSubmit}>
<Modal.Body>
<Form.Group as={Row}>
<Form.Label column sm="2" className="text-right label">
Status
</Form.Label>
<Col sm="9">
<Form.Control
className="input"
as="select"
name="active"
placeholder="Select One"
value={this.state.active}
onChange={this.handleChange}
>
<option>Select One</option>
<option>Y</option>
<option>N</option>
</Form.Control>
</Col>
</Form.Group>
{errMsg}
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.handleCloseModal}>
Cancel
</Button>
<Button variant="primary" type="submit">
Update Changes
</Button>
</Modal.Footer>
</Form>
</Modal>
<Table striped bordered hover>
{header}
<tbody id={'usersTableBody'} >
{clients.map(item => (
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.userName}</td>
<td>{item.companyName}</td>
<td>{item.email}</td>
<td>{item.phone}</td>
<td>{item.active}</td>
<td>
<Button variant="primary" onClick={() =>
this.handleShowModal(
item.id, item.userName, item.active
)} >Edit
</Button>
</td>
</tr>
))}
</tbody>
</Table>
<Footer
page={this.state.page}
total={this.state.total}
offset={100}
handleClick={this.handleClick}
handleNext={this.handleNext}
handlePrevious={this.handlePrevious}
pageCount={pageCount}
/>
</div>
);
}
}
const mapStateToProps = state => (
{
searchOptions: state.searchClientsOption.searchOptions,
userName: state.session.userName,
active: state.session.active,
loadNewData: state.searchClientsOption.loadNewData,
}
);
const mapDispatchToProps = dispatch => ({
onToggleLoadClientsFlag: () => dispatch(doToggleLoadClients()),
setOptions: options => dispatch(doSetOptions(options))
}
);
export default connect(
mapStateToProps,
mapDispatchToProps
)(UsersTable);
几天后,我发现这行代码导致了无限循环,不知何故,loadNewData值不断地来回无限地改变值。
const { loadNewData } = this.props;
if (loadNewData) {
this.setState({
page: 0
}, () => this.updateTable());
}
我的意图是仅在选项发生变化时更新表格,所以我所做的是
if(prevProps.searchOptions != this.props.searchOptions) {
...... }