React数据管理应用列表生成多个独立条目问题求助
Hey there! Let's break down and fix the two main issues you're facing with your CpdList component: inputs showing as separate entries, and data loss/empty items on refresh.
The root cause here is how you're adding new data to your items state. Right now, every time a user enters a value in a prompt (like date, activity, etc.), you're pushing a separate object into the items array. Each of these objects only has one property (e.g., {id: uuid(), date: "01/01/2024"}), so when you map over items, each entry only displays the one property it has, leaving the rest blank—and creating multiple entries instead of one complete entry.
The Fix
Instead of adding a new object for each prompt, collect all the input values first, then add a single object containing all fields to the items array. Here's how to adjust your onClick handler:
onClick={() => { const date = prompt('Enter Date'); const activity = prompt('Enter Activity'); const hours = prompt('Enter Hours'); const learningStatement = prompt('Enter Learning Statement'); const evidence = prompt('Evidence YES or NO!'); // Only add the entry if at least one field is filled (adjust this logic as needed) if (date || activity || hours || learningStatement || evidence) { const newItem = { id: uuid(), date: date || '', activity: activity || '', hours: hours || '', learningStatement: learningStatement || '', evidence: evidence || '' }; this.setState(state => ({ items: [...state.items, newItem] })); } }}
This way, all fields are part of one entry, so when you render each item, all the fields will be displayed in a single ListGroupItem instead of split across multiple entries.
Empty entries on refresh
Your initial state has 5 empty objects in the items array:
state = { items: [ {id: uuid(), date: ''}, {id: uuid(), activity: ''}, {id: uuid(), hours: ''}, {id: uuid(), learningStatement: ''}, {id: uuid(), evidence: ''} ] }
These are what show up as empty entries when you refresh. Remove them and start with an empty array instead:
state = { items: [] }
Data loss on refresh
React state is temporary—it resets when the browser refreshes. To persist your data, you can use localStorage to save and load items. Here's how to implement that:
- Load data from localStorage when the component mounts:
Add acomponentDidMountlifecycle method:
componentDidMount() { const savedItems = localStorage.getItem('cpdItems'); if (savedItems) { this.setState({ items: JSON.parse(savedItems) }); } }
- Save data to localStorage whenever the state updates:
Add acomponentDidUpdatelifecycle method:
componentDidUpdate(prevProps, prevState) { if (prevState.items !== this.state.items) { localStorage.setItem('cpdItems', JSON.stringify(this.state.items)); } }
Full Modified Component
Putting it all together, here's your updated CpdList component:
import React, { Component } from 'react'; import { Container, ListGroup, ListGroupItem, Button } from 'reactstrap'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; import uuid from 'uuid'; export default class CpdList extends Component{ state = { items: [] // Start with empty array } componentDidMount() { // Load saved items from localStorage const savedItems = localStorage.getItem('cpdItems'); if (savedItems) { this.setState({ items: JSON.parse(savedItems) }); } } componentDidUpdate(prevProps, prevState) { // Save items to localStorage whenever state changes if (prevState.items !== this.state.items) { localStorage.setItem('cpdItems', JSON.stringify(this.state.items)); } } render() { const { items } = this.state; return ( <Container> <Button color='dark' style={{marginBottom: '2rem'}} onClick={() => { const date = prompt('Enter Date') const activity = prompt('Enter Activity') const hours = prompt('Enter Hours') const learningStatement = prompt('Enter Learning Statement') const evidence = prompt('Evidence YES or NO!') // Only add if at least one field is provided if (date || activity || hours || learningStatement || evidence) { const newItem = { id: uuid(), date: date || '', activity: activity || '', hours: hours || '', learningStatement: learningStatement || '', evidence: evidence || '' }; this.setState(state => ({ items: [...state.items, newItem] })); } }} >Add Data</Button> <ListGroup> <TransitionGroup className='cpdList'> {items.map(({ id, date, activity, hours, learningStatement, evidence }) => ( <CSSTransition key={id} timeout={500} classNames='fade'> <ListGroupItem> <Button className='remove-btn' color='danger' onClick={() => { this.setState(state => ({ items: state.items.filter(item => item.id !== id) })); }} >×</Button> <ul className="list-group"> <li className="list-group-item">Date: {date || 'Not provided'}</li> <li className="list-group-item">Activity: {activity || 'Not provided'}</li> <li className="list-group-item">Hours: {hours || 'Not provided'}</li> <li className="list-group-item">Learning Statement: {learningStatement || 'Not provided'}</li> <li className="list-group-item">Evidence: {evidence || 'Not provided'}</li> </ul> </ListGroupItem> </CSSTransition> ))} </TransitionGroup> </ListGroup> </Container> ) } };
I also added || 'Not provided' to each list item to make empty fields more user-friendly instead of showing blank text.
内容的提问来源于stack exchange,提问作者Sean van Loggerenberg

