check this code please https://stackblitz.com/edit/react-koqfzp?file=src/Section.js
Everytime i add an item, i'm also adding an random number that i want to edit. The number is rendered at a MUI Text field component.
<TextField
type="number"
variant="standard"
aria-readonly={edit === true ? false : true}
value={edit === true ? value : numbers[i]}
onChange={(e) => setValue(e.target.value)}
/>
And the buttons are rendered based on edit's state, like this:
{edit === true ? (
<button onClick={() => saveEdit(i)}>Save</button>
) : (
<button onClick={() => setEdit(true)}>Edit</button>
)}
And it's working, i can edit it and rerender with the new value, the problem is that when i click the edit button, every field receives the new input value and the save button shows up in every field, though when i save it, it returns to it's original value. How i can do what i'm doing but to just one specific field?
The problem is that you are using setEdit as a boolean
You can define it as the index of the array to edit with -1 as starting value
const [edit, setEdit] = useState(-1)
...
{edit === i ? (
<button onClick={() => saveEdit(i)}>Save</button>
) : (
<button onClick={() => setEdit(i)}>Edit</button>
)}
I recommend creating another component Exemple:Item with it has it's own states edit,value states
And pass the needed props to it which are the value,numbers,saveEdit(index,newValue),removeItem(index) and also the index
saveEdit has to be changed in section by passing the index and the newValue
I hope this clear for you
add the map values in a component and add the states inside the component so you will have multiple states for each component not just 1
parent component
{section.items.map((item, i) => (
<Component key={i} item={item}/>
)}
child component
const Component = ({ section, addItem, removeItem}) => {
const [newItem, setNewItem] = useState('');
const [edit, setEdit] = useState(false);
const [value, setValue] = useState(0);
const [numbers, setNumbers] = useState(section.number);
const handleChange = (e) => {
setNewItem(e.target.value);
};
return (
<TextField
type="number"
variant="standard"
aria-readonly={edit === true ? false : true}
value={edit === true ? value : numbers[i]}
onChange={(e) => setValue(e.target.value)}
/>
)
Related
I am using MUI's autocomplete component to display some of my objects as suggestions.
Everything is working fine, but I am placing an avatar as a start adornment inside of the textfield (inside of renderInput), where I wish to put the value of corresponding image prop of what is selected.
What I want : I want that whatever value is selected in autocomplete, (there is an image property inside my object, whose array i mapped to options prop of autocomplete). So, I want that corresponding image, together with the option label (inside of textfield), the corresponding image should also be shown inside startAdornment.
Well, I want it similar to LinkedIn's company field which we fills while posting a new job.
Here's my MUI Autocomplete, I am new to 'typescript'.
<Autocomplete
open = {open}
onOpen = {() => setOpen(true)}
onClose = {() => setClose(true)}
options = {items.sort((a, b) => -b.name.localeCompare(a.name))}
isOptionEqualToValue = {(option, value) => option.name === value.name}
getOptionLabel = {(option : any) => option.name}
renderInput = {(params : any ) => (
<TextField
{...params}
className = {'inputField'}
placeholder = {'Enter item name'}
label = {'items'}
InputProps = {{
...params.InputProps,
startAdornment : ( <Avatar src = {params.image} /> )
}} />
)}
renderOption = {(props, option) => {
return (
<li
key = {option.id}
{option.name}
</li> )}} />
so I want that whatever name is selected from autocomplete, its corrosponding image should be displayed into the Avatar(inside of startadornment).
How to achieve this?
And, yes, here is my Object array be like -
[{ id : number, name : string, image : string }, {...}, {...}, and so on...];
image here is url of the image, ( that I wish to show into the avatar inside startAdornment.)
Do i need to provide a new state for this?
All suggestions would be appretiated, eagerly looking for answers that fits.
My approach would be to create a state to store the select value and whenever this state changes it would update the avatar component:
const [value, setValue] = useState("");
const AvatarAdornment = useMemo(()=>
<Avatar src={items.find(item =>item.name === value).image} />,
[value, items])
And in your autocomplete component use it like:
<Autocomplete
open = {open}
onOpen = {() => setOpen(true)}
onClose = {() => setClose(true)}
options = {items.sort((a, b) => -b.name.localeCompare(a.name))}
isOptionEqualToValue = {(option, value) => option.name === value.name}
getOptionLabel = {(option : any) => option.name}
renderInput = {(params : any ) => (
<TextField
{...params}
className = {'inputField'}
placeholder = {'Enter item name'}
label = {'items'}
InputProps = {{
...params.InputProps,
startAdornment : ( <AvatarAdornment/> )
}} />
)}
renderOption = {(props, option) => {
return (
<li
key = {option.id}
{option.name}
</li> )}} />
I have this
function Toggle(){
const [toggleOn, setToggleOn] = useState(false);
return (
<div>
{toggleOn == true && <Settingson className={classes.settingson} onClick={() => setToggleOn(d => !d)} value="true" name="toggle"/>}
{toggleOn == false && <Settingsoff className={classes.settingsoff} onClick={() => setToggleOn(d => !d)} value="false" name="toggle"/>}
</div>
)
}
export default Toggle;
And somehow I want to get the value of this
<Toggle />
I tried many things, but couldn't came up with a solution. Please help! I am new to React Next Js.
I tried to get the value of a jsx component, but It doesn't work. Somehow I need to check if the value of the toggle is true or not.
Your component needs to be authored so that it can inform its consuming component that its state has changed: see it as a way data is "emitted" from a child component.
In the example below, I have updated your component so that it accepts a onInput prop, which should be a method. It is invoked whenever the click event is fired, and "piggy-backs" on the state change to toggleOn:
function Toggle({ onInput }){
const [toggleOn, setToggleOn] = useState(false);
const onToggleClick = (value) => {
// Updates internal state
setToggleOn(value);
// Fires the callback passed as a prop
onInput(value);
};
return (
<div>
{toggleOn == true && <Settingson className={classes.settingson} onClick={() => onToggleClick(d => !d)} value="true" name="toggle"/>}
{toggleOn == false && <Settingsoff className={classes.settingsoff} onClick={() => onToggleClick(d => !d)} value="false" name="toggle"/>}
</div>
)
}
export default Toggle;
Then in the parent component it is a matter of passing in a function to the prop, e.g.:
const onToggleInput = (value) => console.log(value);
return <Toggle onInput={onToggleInput} />
Not sure if I get what you are trying to do but I think you can simplify in a similar way
function Toggle(){
const [toggleOn, setToggleOn] = React.useState(false);
const handleToggle = () => {
console.log("toggleOn",toggleOn)
setToggleOn(prev => !prev)
}
return (
<div>
<button onClick={handleToggle} value={toggleOn} name="toggle">something</button>
</div>
)
}
just replace the button with your component Settings, you just need one that handles true/false values or add a conditional ternary operator for conditional rendering.
return toggleOn ? <TrueComponent /> : <FalseComponent />
I have below code where I am checking or unchecking the checkbox based on some conditions, and I came across an issue where I am trying to check the checkbox, and it is failing the first time and from the second time onwards works perfectly.
export const AntdDefaultOverrideInputNumberAdapter = ({
input: { onChange, value },
disabled,
defaultValue,
formatter,
...rest
}) => {
const [isDefaultValueOverriden, setIsDefaultValueOverriden] = useState(
!!value && value !== defaultValue
);
const handleCheckboxChange = () => {
const hasOverride = !isDefaultValueOverriden;
setIsDefaultValueOverriden(hasOverride);
onChange(hasOverride && !!value ? value : defaultValue);
};
const handleOverrideChange = (v) => {
onChange(v);
};
return (
<Badge
offset={[0, -6]}
count={
<div>
<Checkbox
disabled={disabled}
checked={isDefaultValueOverriden}
onChange={handleCheckboxChange}
style={{ zIndex: 10 }}
/>
</div>
}
>
<InputNumber
size="small"
onChange={handleOverrideChange}
disabled={disabled || !isDefaultValueOverriden}
style={{ width: 65 }}
value={isDefaultValueOverriden ? value : defaultValue}
formatter={formatter}
{...rest}
/>
</Badge>
);
};
I am not sure where I am wrong with the above code, The problem only appears on trying to check the checkbox the first time, and it disappears from the second time onwards..
Could anyone suggest any idea on this? Many thanks in advance!!
I am using the "ANTD" library for the checkbox, and the "value" is an empty string, and the defaultValue is "5"
I'm building a controlled form with dynamic fields.
The Parent component get data from a redux store and then set state with the values.
I don't want to make it with too much code lines so I turn the dynamic fields into a component.
States stay in the parent component and I use props to pass the handlechange function.
Parent :
function EditAbout(props) {
const [img, setImg] = useState("");
const [body, setBody] = useState(props.about.body);
const [instagram, setInstagram] = useState(props.about.links.instagram);
const [linkedin, setLinkedIn] = useState(props.about.links.linkedin);
const [press, setPress] = useState(props.about.press)
const handleSubmit = (e) => {
// Submit the change to redux
};
// set states with redux store
useEffect(() => {
setBody(props.about.body);
setInstagram(props.about.links.instagram);
setLinkedIn(props.about.links.linkedin);
setPress(props.about.press);
}, []);
const handleChangeChild = (e, index) => {
e.preventDefault();
let articles = press
const {value, name } = e.target
if (name === "title") {
articles[index].title = value;
} else {
articles[index].link = value;
}
setPress(articles)
console.log(articles[index])
}
return (
<Box>
<h1>CHANGE ABOUT ME</h1>
<Input
label="Image"
name="img"
type="file"
variant="outlined"
margin="normal"
onChange={(e) => setImg(e.target.files)}
/>
<Input
label="body"
value={body}
name="body"
onChange={(e) => setBody(e.target.value)}
variant="outlined"
multiline
rowsMax={12}
margin="normal"
/>
<Input
label="instagram"
value={instagram}
name="instagram"
variant="outlined"
margin="normal"
onChange={(e) => setInstagram(e.target.value)}
/>
<Input
label="Linkedin"
value={linkedin}
name="linkedin"
variant="outlined"
margin="normal"
onChange={(e) => setLinkedIn(e.target.value)}
/>
<Child press={press} onChange={handleChangeChild} />
{props.loading ? (
<CircularProgress color="black" />
) : (
<Button onClick={handleSubmit} variant="contained">
Send
</Button>
)}
</Box>
);
}
Child :
function Child(props) {
const { press, onChange } = props;
const inputsMarkup = () =>
press.map((article, index) => (
<div key={`press${index}`} style={{ display: "flex" }}>
<input
name="title"
value={press[index].title}
onChange={(e) => onChange(e, index)}
/>
<input
name="link"
value={press[index].link}
onChange={(e) => onChange(e, index)}
/>
<button>Delete</button>
</div>
));
return (
<div>
<h1>Press :</h1>
{inputsMarkup()}
</div>
);
}
Everything is fine when I'm typing in the Parent inputs. But when I'm using Child fields state update for one character but come back at its previous state right after.
It also doesn't display the character change. I can only see it in the console.
Thanks you in advance for your help
The problem is that you're mutating the state directly. When you create the articles variable (let articles = press) you don't actually create a copy and articles doesn't actually contain the value. It's only a reference to that value, which points to the object’s location in memory.
So when you update articles[index].title in your handleChangeChild function, you're actually changing the press state too. You might think that's fine, but without calling setPress() React will not be aware of the change. So, although the state value is changed, you won't see it because React won't re-render it.
You need to create a copy of the press array using .map() and create a copy of the updated array element. You can find the updated handleChangeChild() below:
const handleChangeChild = (e, index) => {
e.preventDefault();
const { value, name } = e.target;
setPress(
// .map() returns a new array
press.map((item, i) => {
// if the current item is not the one we need to update, just return it
if (i !== index) {
return item;
}
// create a new object by copying the item
const updatedItem = {
...item,
};
// we can safely update the properties now it won't affect the state
if (name === 'title') {
updatedItem.title = value;
} else {
updatedItem.link = value;
}
return updatedItem;
}),
);
};
I'm making small app in react js which basically would display SELECTED if item is selected.
Here is my code:
import React, { useState } from 'react';
function SelectedFiles(props) {
const [selectedFile, setSelectedFile] = useState(0);
const selectSelectedFileOnChange = id => {
setSelectedFile(id);
props.onSetSelectedFile(id);
};
return (
<MainContainer>
<RadioButton
key={props.id}
value={props.id}
name="Acfile"
onChange={e => {
selectSelectedFileOnChange(props.id);
}}
disabled={false}
></RadioButton>
<span>{props.file.name}</span>
<span>{props.file.size}</span>
<span>{props.file.isPrimary === true ? 'SELECTED' : null}</span>
</MainContainer>
);
}
export default SelectedFiles;
This component is part of parent component and purpose of this component is just to display an items:
<AddF className="modal-body">
{docs && docs.length > 0
? docs.map(file => (
<SelectedFiles
key={file.id}
id={file.id}
file={file}
onSetSelectedFile={handleSetPrimaryFile}
/>
))
: null}
</AddF>
const handleSetPrimaryFile = id => {
props.onSetPrimaryFile(id);
};
As its possible to see guys I dont know how to remove text from NOT SELECTED element..
Thanks guys ! Cheers
Could you please try below and see if it works?
const [files, setFiles] = useState({docs[0].id: true});
<AddF className="modal-body">
{docs && docs.length > 0
? docs.map(file => (
<SelectedFiles
key={file.id}
id={file.id}
file={file}
isPrimary={!!files[file.id]}
onSetSelectedFile={handleSetPrimaryFile}
/>
))
: null}
</AddF>
const handleSetPrimaryFile = id => {
setFiles({[id]: true});
props.onSetPrimaryFile(id);
};
Change SelectedFiles.js as
<span>{props.isPrimary === true ? 'SELECTED' : null}</span>
Hope it helps.
This snippet {props.file.isPrimary === true ? 'SELECTED' : null} is determining when SELECTED should appear. But I don't see where props.file would ever change.
I also see you using both the useState hook and some sort of prop function passed in to handle selection.
The solution is to have some sort of unique identifier for the files (perhaps that's file.id), then check this value on the selectedFile to determine if SELECTED should appear, e.g., props.file.id === selectedFile.