#reactjs #react-hooks
#reactjs #реагирующие крючки
Вопрос:
Я извлек некоторые данные о держателях карт и отрисовал их. Когда я нажимаю кнопку, извлеченные данные просто добавляются к ранее извлеченным данным, а не перезагружают страницу и отображают их. Ниже показано, как это выглядит. Мне нужно повторно отобразить извлеченные данные после нажатия кнопки вместо того, чтобы продолжать добавлять к ним.
*Button click and 5 cardholders renders
cardholder 1
cardholder 2
cardholder 3
cardholder 4
cardholder 5
*If I click the button again same 5 cardholders are added to above 5 cardholders instead of re-render the 5 cardholders
cardholder 1
cardholder 2
cardholder 3
cardholder 4
cardholder 5
cardholder 1
cardholder 2
cardholder 3
cardholder 4
cardholder 5
* Родительский компонент
function SecurityCenter () {
const [serverIp, setServerIp] = useState("")
const [port, setPort] = useState("")
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const [fetchedData, setFetchedData] = useState([])
const handleSubmit = async (e) => {
e.preventDefault()
fetchData()
}
const fetchData = async () => {
try {
const response = await Api.post('/securityCenterApi/connection', {
serverIp: serverIp,
port: port,
username: username,
password: password
})
if (response.data.Rsp) {
const returnedData = response.data.Rsp.Result
setFetchedData(returnedData)
alert('Security Center Connected')
setServerIp("")
setPort("")
setUsername("")
setPassword("")
}
} catch (err) {
console.log(err)
alert('Please enter correct connection information')
}
}
return (
<>
<form onSubmit={handleSubmit}>
<div className='security-center-container'>
<p>Security Center</p>
<div className='server-ip'>
<p>Server IP:</p>
<div className="control">
<input className="input is-info" value={serverIp} onChange={e => setServerIp(e.currentTarget.value)} style={{width: '100%'}} type="text" placeholder="127.0.0.1"/>
</div>
</div>
<div className='port'>
<p>Port:</p>
<div className="control">
<input className="input is-info" value={port} onChange={e => setPort(e.currentTarget.value)} style={{width: '100%'}} type="text" placeholder="5000"/>
</div>
</div>
<div className='username'>
<p>Username</p>
<div className="control">
<input className="input is-info" value={username} onChange={e => setUsername(e.currentTarget.value)} style={{width: '100%'}} type="text" placeholder=""/>
</div>
</div>
<div className='password'>
<p>Password</p>
<div className="control">
<input className="input is-info" value={password} onChange={e => setPassword(e.currentTarget.value)} style={{width: '100%'}} type="text" placeholder=""/>
</div>
</div>
<div className='connect-button'>
<button className="button is-fullwidth is-link" style={{width: '100%'}}>Connect</button>
</div>
</div>
</form>
<div className="new-user-container">
</div>
<UserContainer
fetchedData={fetchedData}
setFetchedData={setFetchedData}
serverIp={serverIp}
setServerIp={setServerIp}
port={port}
setPort={setPort}
username={username}
setUsername={setUsername}
password={password}
setPassword={setPassword}
/>
</>
)
}
export default SecurityCenter
* Дочерний компонент
function UserContainer (props) {
const [cardHolders, setCardHolders] = useState([])
useEffect(() => {
props.fetchedData.map(user => {
try {
const uuid = user.Guid
const response = Api.post(`/securityCenterApi/getCredentials/${uuid}`, {
serverIp: props.serverIp,
port: props.port,
username: props.username,
password: props.password
})
.then(response => {
const cardHolderCredentials = response.data.Rsp.Result.Credentials
const newGuid = Api.post(`/securityCenterApi/getUsers/${cardHolderCredentials}`, {
serverIp: props.serverIp,
port: props.port,
username: props.username,
password: props.password
})
.then(response => {
const userName = response.data.Rsp.Result.Cardholder.Name
const bitLength = response.data.Rsp.Result.Format.BitLength
const facilityCode = response.data.Rsp.Result.Format.Facility
const cardNumber = response.data.Rsp.Result.Format.CardId
setCardHolders(prev => [...prev, {
username: userName,
bitLength: bitLength,
facilityCode: facilityCode,
cardNumber: cardNumber
}])
}
)
})
} catch (err) {
console.log(err)
}
})
}, [props.fetchedData])
const renderCardHolders = () => {
return cardHolders.map((cardHolder, index) => {
return (
<tr key={index}>
<th>{cardHolder.username}</th>
<td>{cardHolder.bitLength}</td>
<td>{cardHolder.facilityCode}</td>
<td>{cardHolder.cardNumber}</td>
</tr>
)
})
}
return (
<table className="table">
<thead>
<tr>
<th><abbr title="name">Cardholder name</abbr></th>
<th><abbr title="wiegand">Wiegand Bit type</abbr></th>
<th><abbr title="facility">Facility Code</abbr></th>
<th><abbr title="card">Card number</abbr></th>
</tr>
</thead>
<tbody>
{renderCardHolders()}
</tbody>
</table>
)
}
export default UserContainer
Заранее спасибо!
Комментарии:
1. Пожалуйста, покажите код кнопки
2. @JordanDavis Я добавил родительский компонент. Думал, что кнопка находится в дочернем компоненте.
3. Вы объединяете предыдущее состояние с результатом, просто удалите
...prev
4. @diedu, когда я удаляю… ранее не отображались все 5 держателей карт, а вместо этого отображался только 1 держатель карты.
Ответ №1:
Измените ...prev
на ...cardHolders
, но если вы не хотите накапливать ...spread
, тогда вообще не делайте этого при обновлении состояния.
Удалите … распространение:
setCardHolders([...cardHolders, {
username: userName,
bitLength: bitLength,
facilityCode: facilityCode,
cardNumber: cardNumber
}])
Оптимизация производительности с помощью React.memo:
const renderCardHolders = () => cardHolders.map(({ username, bitLength, facilityCode, cardNumber}, idx) => {
return <Row
key={idx}
username={username}
bitLength={bitLength}
facilityCode={facilityCode}
cardNumber={cardNumber}/>
})
const Row = React.memo(({ username, bitLength, facilityCode, cardNumber}) => (
<tr key={index}>
<th>{username}</th>
<td>{bitLength}</td>
<td>{facilityCode}</td>
<td>{cardNumber}</td>
</tr>
))
Комментарии:
1. Когда я удаляю … spread, он не отображает все 5 держателей карт, а вместо этого отображается только 1 держатель карты.
2. Я думаю, я знаю, почему это происходит конкатенация. Когда вы смотрите на второй аргумент перехвата useEffect ([props.fetchedData]), состояние не изменяется, когда я снова нажимаю кнопку и просто продолжаю добавлять к предыдущему состоянию. Возможно, мне нужна очистка?
3. @Henry ты разобрался или тебе все еще нужна помощь?
4. Эй, Джордан, я понял это. Я только что добавил функцию очистки, и она решила мою проблему 🙂
Ответ №2:
Я понял это. Состояние оставалось активным даже после рендеринга, и когда я нажал кнопку для повторной выборки держателей карт, информация не была изменена, и именно поэтому она была объединена с предыдущими отображаемыми держателями карт. Я только что добавил простую функцию очистки в свой перехват useEffect, чтобы состояние было завершено после рендеринга.
useEffect(() => {
props.fetchedData.map(user => {
try {
const uuid = user.Guid
const response = Api.post(`/securityCenterApi/getCredentials/${uuid}`, {
serverIp: props.serverIp,
port: props.port,
username: props.username,
password: props.password
})
.then(response => {
const cardHolderCredentials = response.data.Rsp.Result.Credentials
const newGuid = Api.post(`/securityCenterApi/getUsers/${cardHolderCredentials}`, {
serverIp: props.serverIp,
port: props.port,
username: props.username,
password: props.password
})
.then(response => {
console.log(response)
const userName = response.data.Rsp.Result.Cardholder.Name
const bitLength = response.data.Rsp.Result.Format.BitLength
const facilityCode = response.data.Rsp.Result.Format.Facility
const cardNumber = response.data.Rsp.Result.Format.CardId
setCardHolders(cardHolder => [...cardHolder, {
username: userName,
bitLength: bitLength,
facilityCode: facilityCode,
cardNumber: cardNumber
}])
}
)
})
} catch (err) {
console.log(err)
}
})
return () => {
setCardHolders([])
}
}, [props.fetchedData])