Hi there I made simple antd list where user can add, remove edit list.
But the problem is that when i add, edit list verything works as it should.
But when i remove item from a list, antd always remove last item from a list.
Although i can remove any item.
const [employeeList, setEmployeeList] = useState([])
<List
dataSource={employeeList}
renderItem={(item, index) => (
<List.Item id='listItem' >
<Input
id='listItem-input'
defaultValue={item.name}
onChange={(i) => {
employeeList[index].name = i.target.value
setEmployeeList(answerList)
}}
/>
<span className='removeItem'
onClick={() => {
let newList = setEmployeeList
newList[index] = undefined
setEmployeeList(newList.filter(e => e))
}}
>
<CloseOutlined/>
</span>
</List.Item>
)}
/>
I checked in console items in state gets removed, although it still stays in item.list
First of all I would recomend deleting items with splice()
so rather then this 3 lines in onClick (in remove) just
employeeList.splice(index,1)
Edit:
With your approach you probably want:
let newList = employeeList
newList[index] = undefined
Turns out i had to use a keys.
List.Item wasn't registering changes without keys and was just deleting last item, on any change.
<List.Item id='listItem' key={item.answer} >
Related
I have a component that renders a list, and this list of items can be increased. I'm wrapping the list around a <TransitionGroup> to animate new items being added.
There's a message when we don't have items on the list. However, even when items are added, the message won't go away. I tested it without <TransitionGroup> and it works normally. I have a minimal example of the problem on CodeSandbox.
The code for the component is:
const ItemsList = ({ items }) => {
return (
<TransitionGroup component="ul" timeout={400}>
{items.length === 0
? "There is no items"
: items.map((item, i) => (
<CSSTransition key={i} classNames="item-animation">
<li key={i}>{item}</li>
</CSSTransition>
))}
</TransitionGroup>
);
};
After adding an element to the list, the message should disappear, but it's still visible.
My guess is there's something different react-transition-group uses on render, not rendering the full component as we write it. But how to fix this problem?
Try rendering Transition Group component only if items.length !=0
So your code should look something like this:
items.length !=0 ? `render your component` : "There is no item"
I have a row which includes a React-Select Dropdown and an input field. I am trying to Remove a specific row by its index. I am passing the index in my handler function and want to remove both the fields from the row. The input field is getting removed correctly, but the dropdown value is not getting removed from the same row and it deletes the dropdown from the last index.
I am removing the row with the help of index in this handler
Removing the row by its index:
handleRemoveSocial(idx) {
let someArray = this.state.SocialData;
someArray.splice(idx, 1);
this.setState({ SocialData: someArray });
}
I am rendering the Select Dropdown and Textbox with the help of map method, mapping to the array in my state. Now, How can i map the Select dropdown value from the same row when i remove the textbox. I have included the Sandbox link in this post.
{this.state.SocialData.map((Social, idx) => (
<div className="form-group" key={idx}>
<label htmlFor={"socialprofile"} className="control-label">
Social profile
</label>
<div className="form-input-container select-social-link">
<Select
data-id={idx}
className="profile-module-select-container"
classNamePrefix="profile-module-select"
options={options}
onChange={(selected) => {
this.handleSocialNameChange(selected.value, idx);
}}
onMenuOpen={() => {
this.setState({
selectMenuOpen: true
});
}}
onMenuClose={() => {
this.setState({
selectMenuOpen: false
});
}}
components={{
IndicatorSeparator: () => null
}}
placeholder={"Select"}
isSearchable={false}
isClearable={false}
/>
</div>
<div className="form-input-container input-social-link">
<input
type="text"
id={`${SocialData[idx].socialname}-${idx}`}
className="social-form-control"
placeholder={`Social Url - ${Social.socialname}`}
value={SocialData[idx].name}
onChange={(e) =>
this.handleInputVlaueChange(e.target.value, idx)
}
/>
SANDBOX
This is where the problem is happening within handleSocialNameChange() Handler when change event is fired here.
onChange={(selected) => {
this.handleSocialNameChange(selected.value, idx);
}}
Using index as ids is overall not a good idea. Because they mess up things later and we have to add additional logic to the code as well.
We can use uuid() library or a trick new Date().getTime().toString() for our Ids efficiently.
Working code of CODESANDBOX Link:
https://codesandbox.io/s/reasontosmile-n6ww4?file=/src/App.js
Enjoy :)
You cannot set the index value as key. That will cause issues when adding and removing elements.
Also, don't use array.splice use array.filter. The splice will mutate the original state array.
Currently I'm working on Alarm clock app and I want to do it in way so you can add multiple Alarms. Every Alarm is stored in alarms:[] state in App.js file. If alarm is added,I want to display that alarm as a li element under the clock and I want to be able to remove it by clicking on X icon. Also ,when current time === time set for alarm ,Ring.js component renders and alarm starts ringing. When alarm is ringing there is 'turn off' button. How is it possible to delete this specific alarm which is ringing ,from state array after click on turn off button?
I've tried to send removeAlarm function and alarm(which may help in determining which alarm from array delete)as a prop to this component when condition if fulfilled.
function checkTime(){
if(time.alarms[0]){
const alarms = time.alarms.map(function(alarm,i){
if(time.currentHour === alarm.hour && time.currentMinute === alarm.minute && time.currentSecond
>= 0){
return <Ring message={alarm.message} key={i} alarm={alarm} removeAlarm={removeAlarm} />
}
})
return alarms;
}
}
removeAlarm function:
function removeAlarm(alarm){
setTime(prevState => ({
...prevState,
alarms:[...prevState.alarms.filter(el => el !== alarm)]
}))
}
Ring.js file
let message = props.message;
function removeAlarm(alarm){
props.removeAlarm(alarm);
}
function turnOff(e,alarm){
e.preventDefault();
setShowRing(false);
removeAlarm(alarm);
}
<form>
<h3>{message}</h3>
<button onClick={turnOff}>TURN OFF</button>
</form>
I can't figure it out how to do that. I don't know how to use that passed function or how to determine in that onClick function that THIS is that button which has to be send to removeAlarm function ,map thru state and remove that specific one.
Also second problem which I've noticed is with React Spring Transitions. I'm using it in Ring.js,Alarm.js and want to use it also for listing active alarms in ListAlarms.js. I'm using it the exact same way as in first two components but for ListAlarms.js it's not working and I don't undestand why. My goal is to display those active alarms with transitions not just 'blink' there.
Thank you.
CodeSandBox link here
OK some corrections but you have to alter the transitions
First of all you need to filter your list by id, in order to remove correctly the alarm.
function removeAlarm(alarm){
setTime(prevState => ({
...prevState,
alarms:[...prevState.alarms.filter(el => el.id !== alarm.id)]
}))
}
Secondly, I have removed the from property from your transition, since every new object was positioned on top of others. Also, instead of null for the key I used mapping to each item's id.
(item) => item.id
Finally I corrected the order in map function
{listAlarmTransitions.map(({ item, props, key }) => (
So it became
const listAlarmTransitions = useTransition(props.alarms, (item) => item.id, {
enter: { opacity: 1 },
leave: { opacity: 0 }
});
return (
<ul>
{listAlarmTransitions.map(({ item, props, key }) => (
<animated.div key={key} style={props}>
<li
key={item.id}
id={item.id}
onClick={() => {
removeAlarm(item);
}}
>
<FontAwesomeIcon icon={faTimesCircle} className="listIcon" />
<h3>{item.message}</h3>
<span>
{item.hour}:{item.minute}
</span>
</li>
</animated.div>
))}
</ul>
);
Check this sandbox
https://codesandbox.io/s/broken-morning-upqwp
You are filtering out objects/references you should filter out by id.
Your passed alarm argument is an object and your alarms filter array contains objects, find a unique property which you can filter against, by looking at your code, it should be id.
Something like this:
function removeAlarm(alarm){
setTime(prevState => ({
...prevState,
alarms:[...prevState.alarms.filter(el => el.id !== alarm.id)]
}))
}
I want to add underline to menu item when its active. All works fine, but when I click on an item, its previous classes received from the ReactTransitionGroup add-on component are reset. For example when I click second item the classes will be reset and only active will remain. I want the active class to be insert to existing without cleaning the previous ones.
The .active has ::after pseudo-class
const NavItems = (props) => {
const items = ["section1", "section2", "section3", "section4", "section5"];
const [activeItem, setActive] = useState(0);
return (
<>
<NavItemsOverlay open={props.open} />
<ScollLinks open={props.open}>
{items.map((item, index) => {
return (
<CSSTransition
in={props.open}
key={index}
timeout={{enter: 100 * index, exit: 0 }}
classNames="fade ">
<Link
className={activeItem === index ? " active" : ""}
onClick={() => setActive(index)} >
{item}
</Link>
</CSSTransition>
);
})}
</ScollLinks>
</>
);
};
You appear to be running into issue #318, which is still open. The person posting the issue thinks it's a bug, and the CSSTransition documentation does say:
A few details to note about how these classes are applied:
They are joined with the ones that are already defined on the child component, so if you want to add some base styles, you can use className without worrying that it will be overridden.
...so yeah, that sounds like a bug.
The best way to solve it would be to fork the project, fix the bug, and send a PR. :-)
A really hacky way to work around it would be to use a data-* attribute instead of a class:
<Link
data-cls={activeItem === index ? " active" : ""}
onClick={() => setActive(index)} >
{item}
</Link>
And then in the CSS, instead of:
.active::after {
/* ... */
}
You'd have
[data-cls~=active]::after {
/* ... */
}
That uses class-like attribute matching to match that element.
I have an array that creates a mapping of items with checkboxes. each item has a checked state:
const [checked, setChcked] = React.useState(false)
So the user checks say 5 out of the 20 checkboxes and then press a button (the button is in the higher component, where there is a mapping that creates this items with checkboxes) and it works as intended. But, after the button is pressed and the modal is closing, after I open the modal again, these 5 checkboxes are still checked. I want them to restart to be unchecked just like when I refresh and the state vanishes. Now, I am aware of techniques such as not saving state per each item and just saving the state of the array of items in the higher component but I am confused as I have heard that hooks were created so that it is good practice to sometime save state in dumb components.
Is there a simpler function to just restart to initial value?
Edit:
adding the code
<div>
{policyVersionItems.map(item=> (
<PolicyVersionItem
key={pv.version}
policyVersionNumber={item.version}
policyVersionId={item._id}
handleCheck={handleCheck}
>
{' '}
</PolicyVersionItem>
))}
</div>
And the item
const PolicyVersionItem: React.FunctionComponent<PolicyVersionItemProps> = props => {
const { , policyVersionNumber, policyVersionId, handleCheck } = props
const [checked, setChcked] = React.useState(false)
return (
<Wrapper>
<Label dark={isEnabled}> Version {policyVersionNumber}</Label>
<Checkbox
checked={checked}
onClick={() => {
if (isEnabled || checked) {
setChcked(!checked)
handleCheck(policyVersionId, !checked)
}
}}
/>
</Wrapper>
)
}
Some of it is not relevant. the handle check function is a function that returns data to the higher component from the lower component for example.