Remove uploaded documents from list - javascript

I currently have a document uploader and when a document is uploaded and save is clicked it will render a saved documents summary panel. Basically what I'm wanting to do is onclick of the DeleteIconSmall I want to remove the document name from the summary.
const SavedDocument = ({ document }: { document: DocumentUploaderFile }) => {
return (
<SavedDocumentWrapper>
<StepCompleteIconSmall />
<SavedDocumentName>{document.name}</SavedDocumentName>
<DeleteIconSmall />
</SavedDocumentWrapper>
)
}

If you're looking for a general approach, I'd provide an onDelete prop to SavedDocument handle removal in the parent. If you just want to clear document.name based on whether that icon's been clicked - all inside this component - you'll want to add a click handler to the icon and store state that tracks whether the icon's been clicked, then choose not to render the name if that state variable is true.
Assuming there is an onClick prop on your delete icon (if there's not you may need to wrap the icon with something that does handle clicks), something like this might work:
const SavedDocument = ({ document }: { document: DocumentUploaderFile }) => {
const [hasDeletedDocument, setHasDeletedDocument] = useState(false);
return (
<SavedDocumentWrapper>
<StepCompleteIconSmall />
<SavedDocumentName>{hasDeletedDocument ? '' : document.name}</SavedDocumentName>
<DeleteIconSmall onClick={() => setHasDeletedDocument(false)} disabled={hasDeletedDocument} />
</SavedDocumentWrapper>
)
}

Related

How to add a class of "active" to individual elements in a functional navigation component in react?

This is something quite simple but somehow resulted in a crazy rabbit hole.
This link shows what I want:
https://www.w3schools.com/howto/howto_js_active_element.asp
Nothing special, now the thing becomes hairy for me when the elements in the navbar are rendered from an array of objects (from the specs). The approach I am following is basically rendering a list of buttons, this list of buttons is the state, since supposedly when you update a state it triggers a re-render, then when a button is clicked it "sets" the active class to false on the entire array-state then activates it only for the clicked one. So far it works.
The problem is that the active class is rendered two steps behind. One for the moment when the class in the array-state's elements are set to false, the other when the clicked element gets updated.
As far as I understand useState and setState are queues, hence those are applied asynchronously on each render, in order to avoid that and get the renders to show the current state, useEffect is utilized.
Now the thing is that I am not sure how to apply useEffect in order to achieve the immediate render of the "active" class.
This is the code I have:
import { options } from 'somewhere...'
export default function SideMenu(props){
let auxArr = []
let targetName
const [stateOptions, setStateOptions] = useState([...options])
const [currentOption, SetCurrentOption] = useState({})
function activeOption(e){
// this helps with event bubbling
if (e.target.tagName == "P" || e.target.tagName == "SPAN"){
targetName = e.target.parentElement.id
} else if (e.target.tagName == "IMG"){
targetName = e.target.parentElement.parentElement.id
} else {
targetName = e.target.id
}
// since the main state is an array of objects I am updating it
// in three steps, first the current object is "activated"
// then the main array-state gets "inactivated" to erase all
// the previous "active" classes, finally the activated object
// replaces the corresponding inactive object in the main state.
let targetElement = stateOptions.filter(e => e.id==targetName)[0]
SetCurrentOption({
id: targetElement.id,
activity:true,
img: targetElement.img,
name: targetElement.name
})
// first the "classes" are set to false, then the
// "activated" object replaces the corresponding one
// in the main object, from here comes the two
// steps delay.
auxArr = [...stateOptions]
auxArr.forEach(e => e.activity=false)
setStateOptions(auxArr)
const newOptions = stateOptions.map(e =>
e.id==currentOption.id ? currentOption : e
)
setStateOptions(newOptions)
}
return(
<aside className={styles.sideDiv}>
<nav>
{stateOptions.map(({id, img, name, activity, link}) => {
return(
<button key={id} id={id} onClick={activeOption} className={activity?styles.active:""}>
<Image src={img}/>
<p className={timeColor.theme}> {name} </p>
</button>
)
})}
</nav>
</aside>
)
}
Thanks in advance for any help you can provide.

How to remove value from state array on click

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

How to ensure state is not stale in React hook

The buttons i create using below seems to lag in the selectedButtonIdx value.
Is the toggleSelected not complete by the time getClass is called ?
function ButtonGroup(props) {
const [selectedButtonIdx,setIdx]=useState(props.loadCurrentAsIndex);
const toggleSelected = (e) => {
setIdx(parseInt(e.target.dataset.index));
props.onclick(e);
};
const getClass = (index) => {
return (selectedButtonIdx === index) ? classnames('current', props.btnClass)
: classnames(props.btnClass)
};
let buttons = props.buttons.map((b, idx) => <Button key={idx} value={b.value} index={idx} text={b.text}
onclick={e => toggleSelected(e)}
btnClass={getClass(idx)}/>);
return (
<div>
{buttons}
</div>
);
}
Every onclick is expected to show the user which button in the group was clicked by changing its class.
By looking at this,
<Button
key={idx}
value={b.value}
index={idx}
text={b.text}
onclick={e => toggleSelected(e)}
btnClass={getClass(idx)}
/>
Button is your custom component,
Two things to notice here,
You have provided onclick (c is small) props, in you actual component it should be onClick={props.onclick}
You have used e.target.dataset.index, to work with dataset we should have attribute with data- prefix. So your index should be data-index in your actual component.
So finally your Button component should be,
const Button = (props) => {
return <button text={props.text} data-index={props.index} onClick={props.onclick} className={props.btnClass}>{props.value}</button>
}
Demo
The function setIdx, returned from useState is asynchronous, this means that it may be not be finished by the time you run your next function (as you guessed).
Take a look at useEffect it allows you to specify a function to run once an item in your state changes, this method will ensure your functions are called in the right order.
By now I don't see anything wrong here.
How it works:
initial render happens, onClick event listener is bound
user clicks a button, event handler calls setIdx triggering new render
new render is initiated, brand new selectedButtonIdx is used for rendering(and for getClass call as well)
See, there is no reason to worry about if setIdx is sync function or async.

Dynamically adding input fields and keeping track of what was entered

I am wanting to dynamically create input field values for each category a user creates, the issue is how can I keep track of what the user enters into the input field. As I cannot create X amount of states as it is dynamic. Any tips would be much appreciated, my code is shown below:
var categories = newData.map((category,index) => {
console.log(category)
return (
<div className="content row marginCenter" key={category._id}>
<p>{category.category}</p>
<input type="text" /> //How do I keep track of what was entered for this input field??
<button onClick={() => this.addCategoryLink(category._id)}>Add
link</button>
</div>
)
})
I am wondering how to bind that to the button element
The React docs have a section related to the core of this question:
https://reactjs.org/docs/handling-events.html#passing-arguments-to-event-handlers
Assuming your state holds an array of "categories" objects- essentially, I think what you're looking for boils down to something like this in your map function:
{this.state.categories.map(category => (
<input
type="text"
onChange={event => this.handleCategoryChange(category, event)}
value={category.value}
/>
)}
And then a change handler that looks something like this:
handleCategoryChange = (category, event) => {
const value = event.currentTarget.value;
this.setState(state => {
// Create a copy of the categories array:
const categories = [...state.categories];
// Create a copy of the category, with an updated value:
categories[category.index] = {
...category,
value
};
// Update state with the new values:
return { categories };
});
};
Here's a simple demo:
https://codesandbox.io/s/woqpwvl777
i have other Way for doing this , Of course this way just working well in some situation , forExample when you have just 1 or 3 value
i think you wanna create Input , and there Input are dynamic , and you want define that , if user click in first Button , you get and use first TextInput (value)
in my way ( again i say this : this way just well in some situation ) , we Create data Json like this
[
{ id: n ,
category: 'some',
value: ''
}
in this structure Value key , in the mounting contain nothing or null value if the Value not defined before
for now i create one handler method and this method, called after onChange Event fired on
<input onChange={(e) => this.getValue(category.id,e)} />
that element , this means when user start fill input onChange event handle function and update your state
getValue(id,e) {
let thisId = id-1;
let vs = this.state.c;
vs[thisId].value = e.target.value;
this.setState({
c:vs
});
let v = this.state.c[thisId];
console.log(v);
}
i create Pen in this address -> https://codepen.io/hamidrezanikoonia/pen/vRyJRx?editors=1111
you can check console , for more details ( open console tab in codepen )
and for more details , i create two method , the first fired when input (text) filled ( onChange event ) , and the other fired when clicked on button ( click event )

Issues with updating the State - React

I'm having issues in updating the state values, I'm rendering a external component using Map, and hence not able to access this. So on click of the component I'm not able to call the handleClick function to update the state values..
Here is the state :
this.state = {
attributes : {
hours : {
},
cost : 0,
amenities : defaultAmenities
},
primary_category : "General"
}
Where defaultAmenities is a external file with large javascript object.
The render function :
render() {
let basicAmenities, extendedAmenities
let basicAmenitiesList = [], extendedAmenitiesList = []
//Wrong way of storing this
let _this = this;
}
... More Logics / Switch Cases ...
let amenitiesList = basicAmenitiesList.map(function(item, index){
return <Attribute key={index} name={item.amenity_id} type={item.title} icon={item.icon} selected={item.isSelected} value="" onClick={_this.handleClick.bind(_this)}/>
})
And the attribute component
<div className="attribute-grid" onClick={this.props.onClick}>
...
</div>
Handle click is a function to setState on click of Attribute.
handleClick(e) {
console.log(e.target);
}
On click of the attribute, I need to update the state. The result of console log is attached below. I need to target the input values, but since it return the entire div, how do i get the values of name/value/placeholder?
<div class="attribute-grid-block" data-reactid=".0.2.0.3.0.1.$0.0"><div class="attribute-grid-img" data-reactid=".0.2.0.3.0.1.$0.0.0"><img src="petsIcon" data-reactid=".0.2.0.3.0.1.$0.0.0.0"></div><div class="attribute-grid-info" data-reactid=".0.2.0.3.0.1.$0.0.1"><h6 data-reactid=".0.2.0.3.0.1.$0.0.1.0">Pets</h6><input type="text" name="pets" placeholder="NO INFO FOUND" value="" disabled="" data-reactid=".0.2.0.3.0.1.$0.0.1.1"></div></div>
you can get what you need from the target. but you need to set the onClick on the element that you want it to be the target and then you will have it:
handleClick(e) {
const name = e.target.name;
const value = e.target.value;
const placeholder = e.target.placeholder;
console.log(placeholder);
}
if you want to set the onClick elsewhere you will need to send the values you want, so inside Attribute component you will have a function that will be invoke on click and call the this.props.onClick({ name: '', value: ''});
if you need to use this inside this function, and you are using react with classes. you can write this:
handleClick = (e) => {
console.log(this);
}

Categories

Resources