How to update specific object value of an array in javascript - javascript

I have an array of objects, I did a map on that array now I want to change specific object values. I used onChange method inside map() function. It not working properly could someone please help me to achieve this problem solution.
thanks
localPreRoutingCall &&
localPreRoutingCall.map((item, index) => (
<>
<div className="pre-route-title">{item && item.field_title}</div>
<textarea
placeholder="Enter something"
className="pre-route-textarea"
value={item && item.value}
onChange={(e) => {
e.persist();
changeObjectValue(e.target.value, item);
}}
/>
<div className="pre-route-description">
{item && item.description}
</div>
</>
))
A function where I am updating object value
const changeObjectValue = (value, item) => {
console.log("## array", value);
let localArray = localPreRoutingCall;
var index = _.findIndex(localArray, { id: item.id });
localArray.splice(index, 1, { ...item, value: value });
setLocalPreRoutingCall(localArray);
};

Like said in the comments, you have to pass a key prop to what you're rendering so React knows what changes.
I don't see a reason for you to use the e.persist function when you're passing the value immediately.
import { Fragment } from "react";
localPreRoutingCall?.map((item, i) => (
<Fragment key={item.id}>
<div className="pre-route-title">{item.field_title}</div>
<textarea
placeholder="Enter something"
className="pre-route-textarea"
value={item.value}
onChange={(e) => changeObjectValue(e.target.value, i)}
/>
<div className="pre-route-description">
{item.description}
</div>
</Fragment>
))
You also didn't clone the localPreRoutingCall state array before changing its value.
Another reason why React won't know what changed.
const changeObjectValue = (value, i) => {
const localArray = [...localPreRoutingCall];
localArray[i].value = value;
setLocalPreRoutingCall(localArray);
};

One obvious problem I can see is that you aren't giving the mapped components keys.
<> can't be used in map because it can't be passed a key, so this would be an improvement
<React.Fragment key={item.id}>

Instead of using
localArray.splice(index, 1, { ...item, value: value });
Use
localArray[index].value = value

Related

How can i get the Id of the Todo

am trying to delete am item by id but i keep get error, that each child should have a unique key after giving a it an id, what am i doing wrongly, why am i not getting the id
const TodoList = () => {
const [input, setInput] = useState("");
const [todos, setTodos] = useState([])
const handleSubmit = e => {
e.preventDefault()
setTodos([...todos, input])
setInput("")
}
const handleDelete = id => {
let item = todos.filter(todo => todo.id !== id)
console.log(item)
// setTodos(item)
}
return (
<div className='todolist'>
<h2>Todo List</h2>
<form>
<input value={input} onChange={e => setInput(e.target.value)} placeholder='write something' />
<button onClick={handleSubmit}>Add todo</button>
</form>
{todos.map(todo => (
<div key={todo.id} className='todolist__details'>
<h2>{todo}</h2>
<DeleteIcon onClick={() => handleDelete(todo.id)} />
</div>
))}
</div>
);
};
export default TodoList;
From the above code, it looks like todos is an array of strings. So when you are assigning key by doing todo.id, you are assigning the key to be undefined since the id property does not exist in the string type. Change you map method like this
{todos.map((todo, i) => (
<div key={i} className='todolist__details'>
<h2>{todo}</h2>
<DeleteIcon onClick={() => handleDelete(i)} />
</div>
))}
and then change on your handleDelete like
const handleDelete = id => {
const newTodos = [...todos];
newTodos.splice(id, 1);
console.log(newTodos)
setTodos(newTodos)
}
{todos.map((todo, i) => {
<div key={i} className='todolist__details'>
<h2>{todo}</h2>
<DeleteIcon onClick={() => handleDelete(i)} />
</div>
})}
You can check how list and keys work in react in here
https://reactjs.org/docs/lists-and-keys.html
You have not given id to any of your todo , Pass the index of that todo instead of id , it will solve your problem
Try this sandbox code link
I hope you find this helpful.
As other users (especially TheWhiteFang) pointed out above, your todos list is an array, and your single todo item inside this array is string which you get from your input.
Alternatively, you could set your single todo item as an object instead of plain string, such as {id: 1, content: input}, for example, change the line of setTodos to:
setTodos([...todos, { id: count, content: input }]);
In this way, you could then access the id of every single todo item and access the content of the todo item via todo.content, when using a map function.
To illustrate this, you may refer to this code snippet:
https://codesandbox.io/s/todo-list-in-5min-2tomwb?file=/src/App.js

How do I stop duplication of an object in localStorage?

I've a component call KeywordLocation.js, and It has one prop named location.
this component is a mapped array and on click I want to save the object of location in localStorage. I created here an empty array and pushing the object on every click. For now I'm getting 5 mapped location objects. when I click on any of them, it saves the object but on 2nd click it doesn't stop duplicating the object. How do I stop this duplication?
searchedLocation.map((location, i) => {
return (
<KeywordLocation
setShowMap={props.setShowMap}
location={location}
key={i}
getPositionFromManualSearch={props.getPositionFromManualSearch}
/>
);
});
KeywordLocation.js
const Component = ({ location }) => {
let allSearchedLocations = [];
const redirectToMap = async () => {
allSearchedLocations.push(location);
allSearchedLocations = allSearchedLocations.concat(
JSON.parse(localStorage.getItem("recent_location_searched") || "[]")
);
const previousLocation = JSON.parse(
localStorage.getItem("recent_location_searched")
);
console.log(previousLocation);
localStorage.setItem(
"recent_location_searched",
JSON.stringify(allSearchedLocations)
);
};
return (
<div onClick={() => redirectToMap()} className="pt-md cursor-pointer">
<p>{location.structured_formatting.main_text}</p>
<p className="text-xs border-b border-black pb-md ">
{location.description}
</p>
</div>
);
};
Are you entirely sure the duplication is ocurring on local storage?
As long as you use the same key, recent_location_searched, there will be only one value stored on that key. Take a look at the "Storage" tab on your browser's debug console to see what's actually being stored.
All evidence seems to point that the duplication is ocurring at the searchLocations variable, not atlocalStorage.
You might try to add some conditional logic that prevents you from pushing to searchLocations if the location is the same as the one on the last item on the array.
The problem is not related to localStorage but more about the usage of the array structure. You could rely on JavaScripts object to store the unique values. You lose the insertion order but you can create a companion array that keep a reference to the order.
const Test = ({ location }) => {
const redirectToMap = () => {
const locations =
JSON.parse(localStorage.getItem("recent_location_searched")) || {};
locations[location.name] = location;
localStorage.setItem("recent_location_searched", JSON.stringify(locations));
};
return (
<div onClick={() => redirectToMap()} className="pt-md cursor-pointer">
<p>{location.name}</p>
</div>
);
};
export default function App() {
const data =
JSON.parse(localStorage.getItem("recent_location_searched")) || {};
return (
<div>
<div className="App">
{[
{ name: "location1" },
{ name: "location3" },
{ name: "location2" }
].map((location) => (
<Test key={location.name} location={location} />
))}
</div>
<ul>
{Object.values(data).map((location) => (
<li key={location.name}>Saved {location.name}</li>
))}
</ul>
</div>
);
}

How Can I make these inputs editable but have them set to a default value- react?

I have an array of objects and I need the keys and values to be editable, I was given this approach : https://codesandbox.io/s/silly-gagarin-j8cfi?file=/src/App.js
But as you can see, the inputs are all empty.
I have tried using defualtValue but that will cause problems later I believe. The aim of this is to eventually compare these values to a database later on.
Please can someone take a look at the sandbox I linked above and help me out?
You need to use Object.entries which will give you access to both the key and value of each property. Your example was not working because you were trying to destructure Object.values as an object when it returns an array.
See my working code below:
https://codesandbox.io/s/inspiring-swirles-208cm?file=/src/App.js
export default function App() {
const [data, setData] = useState(baseData);
const updateValue = (index) => (e) => {
setData((prev) => {
const copiedData = [...prev];
copiedData[index][e.target.name] = e.target.value;
return copiedData;
});
};
return (
<>
<div>
{data.map((item, index) => (
<div key={item.id}>
{Object.entries(item).map(([key, value]) => (
<input
name={key}
type="text"
id={item.id}
key={item.id}
value={value}
onChange={updateValue(index)}
/>
))}
</div>
))}
</div>
</>
);
}

how to update material ui textfield correctly onChange and put value into Json object?

I am trying to update the title and body objects within my array with the value provided from the input fields that mapped to the array length. The body object updates perfectly but the title updates with only the current input value. For instance, if I were to try and type "the".
The title object would change as follows "title: "t"" => "title: "h""
=> "title: "e".
Desired output would be "title: "t"" => "title: "th"" => "title:
"the".
This works with the body so I am confused why it is not with the title maybe I have missed something.
export const NewsArticlesPage = () => {
const [newsArticlesJson, setNewsArticlesJson] = useContext(
NewsArticlesContext
)
const [numberOfSteps, setNumberOfSteps] = useState(0)
const stepsMap = Array.apply(
null,
Array(numberOfSteps).fill({ title: '', body: '' })
)
let stepsArray = { ...stepsMap } as StepsMapProps
const updateTileArray = (index: number, titleEventData: string) => {
const stepsArrayData = { ...stepsArray }
stepsArray = {
...stepsArrayData,
[index]: { ...stepsArrayData[index], title: titleEventData },
}
console.log(stepsArray[index])
console.log(stepsArray[index]?.title)
}
const updateRichTextArray = (index: number, richTextEventData: string) => {
const stepsArrayData = { ...stepsArray }
stepsArray = {
...stepsArrayData,
[index]: { ...stepsArrayData[index], body: richTextEventData },
}
console.log(stepsArray[index])
console.log(stepsArray[index]?.body)
}
return (
<NewsArticlesWrapper>
<TextField
type="number"
label="Number of steps"
value={numberOfSteps}
InputProps={{ inputProps: { min: 0 } }}
onChange={(e) => setNumberOfSteps(Number(e.target.value))}
/>
{stepsMap.map((n, index) => (
<>
<Typography key={'heading' + index}>Step: {index + 1}</Typography>
<TextField
key={'title' + index}
type="text"
label="Title"
value={stepsArray[index]?.title}
onChange={(titleEventData) =>
updateTileArray(index, titleEventData.target.value)
}
/>
<ReactQuill
key={'quill' + index}
theme="snow"
value={stepsArray[index]?.body}
modules={modules}
onChange={(richTextEventData) =>
updateRichTextArray(index, richTextEventData)
}
/>
</>
))}
<Button
variant="contained"
colour="primary"
size="medium"
onClick={updateNewsArticleJson}
>
Submit Article
</Button>
</NewsArticlesWrapper>
)
}
If any extra information is needed to help me please ask.
Code Sandbox set up to replicate issue: https://codesandbox.io/embed/kind-buck-shg2t?fontsize=14&hidenavigation=1&theme=dark
<TextField /> and <ReactQuill /> input components are defined in a ‘controlled component’ way. The object they’re setting / getting values to is stepsArray which you have created as a normal object
let stepsArray = { ...stepsMap } as StepsMapProps
The onChange handler on both these components are changing values on this object
stepsArray = {
...stepsArrayData,
[index]: { ...stepsArrayData[index], title: titleEventData },
}
but, there isn't a re-render, because React re-renders when state changes. stepsArray is a normal js object, and although the value is changing, it is not causing a re-render. This is the reason, you cannot see the value you type on Title <TextField />component.
To solve this, make the object where you’re setting/getting values to, with useState, and use the set(State) function provided by the useState hook accordingly to update values inside the object. This will cause a re-render whenever input's value changes, because now you will be changing the object created through useState's setter function and your UI will be in sync with the state.

Why won't my controlled Input component in React update with the state?

I've got a list of ingredients in an "editIngreds" array in my state, e.g. ['Apple', 'Banana', 'Orange']. They are rendered to the DOM via react map function. I have an edit mode that replaces the text with Input boxes so I can edit the items and save them back to the state array. However, when typing in the box, nothing happens. console.log shows that editIngreds[i] is indeed being updated when I type a character, however the input boxes do not update with this new value.
export default function RecipeCard({ recipe, onEditRecipe, onRemoveRecipe }) {
const ingredients = Object.values(recipe.ingredients);
const ingredientKeys = Object.keys(recipe.ingredients);
const [editIngreds, setEditIngreds] = React.useState(ingredients)
...
const onChangeEditIngreds = (event, i) => {
const value = event.target.value;
const ingreds = editIngreds
ingreds[i] = value;
setEditIngreds(ingreds);
};
...
return (
<ul>
{ingredients.map((ingred, i) => (
<li key={ingredientKeys[i]}>
{editMode ? (
<Input
type="text"
value={editIngreds[i]}
onChange={(e) => onChangeEditIngreds(e,i)}
/>
) : (
<>{ingred}</>
)}
</li>
))}
</ul>
You are mutating the state. Make a shallow copy of ingredients before updating
const onChangeEditIngreds = (event, i) => {
const value = event.target.value;
const ingreds = [...editIngreds];
ingreds[i] = value;
setEditIngreds(ingreds);
};
Rather than using this -> ingredients.map((ingred, i)
use this ->
editIngreds.map((ingred, i){
....
<Input
type="text"
value={ingred}
onChange={(e) => onChangeEditIngreds(e,i)}
/>

Categories

Resources