Реагирует на то, что выбранные данные сохраняются в цепочке с ранее выбранными данными

#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])