Using Objects in React - javascript

I am recieving an object from a websocket,
const client = new
W3CWebSocket("ws://ABCD:9080/user");
I want to access values from the object and display it on the browser.
const [object, setObject] = useState("");
client.onopen = () => {
console.log("Connected");
};
client.onmessage = (e) => {
const newObj = JSON.parse(e.data);
Next I want to set new state with the object I recieved.
setObject(newObj.data);
Next, I want to map through that object and access the values inside it:
return (
<div className="App">
<Navbar />
{Object.keys(object).map((objKey, index) => (
<div key={index}>
<p> {objKey} : {object[objKey]}</p>
</div>
))}
<DataTable object = { object } />
</div>
);
How do I map through the object and display the values I need on the browser.
I think I'm missing something since nothing is getting displayed n my browser.

You have used Object.keys(setObject) where, setObject is a function, and will return an empty array [].
Use Object.keys(object) instead
return (
<div className="App">
<Navbar />
{Object.keys(object).map((objKey, index) => (
<div key={index}>
<p> {objKey} : {object[objKey]}</p>
</div>
))}
<DataTable object = { object } />
</div>
);

You are passing setObject instead of object in Object.keys()

Related

React key error although all elements have unique identifier

So I am getting used to React and I understand when using the map function each item needs to be assigned a unique key. I believe I am doing this correctly
return (
countries.map(country => {
return <>
<div key={country.name.common}>
{country.name.common} <button onClick={() => setCountries([country])}>show</button>
</div>
</>
})
)
I still receive an error in the console saying:
Warning: Each child in a list should have a unique "key" prop.
at CountryList.
at div.
Where CountryList being the file the code is extracted from. I have tried adding the same key to the button element and also tried giving the button element its own unique key.
descriptive key error message
countries is a call to "https://restcountries.com/v3.1/all" api.
useEffect(() => {
axios.get("https://restcountries.com/v3.1/all")
.then(response => setCountries(response.data))
}, [])
const handleFilterChange = (event) => {
setFilter(event.target.value)
const filtered = countries.filter(country => country.name.common.toLowerCase().includes(event.target.value))
setCountries(filtered)
}
console error message along with what the app looks like
The key must be placed on the parent element that you are returning from the map function.
Since it's a fragment in this case, you can't directly assign a key to it, unless you use the actual fragment component.
// Can't assign a key
<></>
// Can assign a key
<React.Fragment key={...}></React.Fragment>
Then again, if you only have the div here, why do you need the fragment?
A shorter syntax of your code would look like this:
return (
countries.map(country => (
<div key={country.name.common}>
{country.name.common}
<button onClick={() => setCountries([country])}>show</button>
</div>
))
)
You are using <> as a perent tag and add key in child <div> tag. remove <></> as you dont need this or use Fragment insted
return countries.map(country => (
<div key={country.name.common}>
{country.name.common}
<button onClick={() => setCountries([country])}>show</button>
</div>
))
or
return countries.map(country => {
return (
<React.Fragment key={country.name.common}>
<div>
{country.name.common} <button onClick={() => setCountries([country])}>show</button>
</div>
</React.Fragment>
)
})

How to update specific object value of an array in 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

How to map over an array of objects in another file in React?

I am trying to refactor my code and in doing so, I am extracting a single item and putting it into its own component. This MemberItem component has multiple functions state that influence its rendering, however, when I start passing props, the component breaks. I am passing all of the functions, properties and state the the child component needs, but I am still unable to get it to render properly.
// Members.js (Parent Component)
export const Members = () => {
// BELOW ARE THE FUNCTIONS AND STATE THAT INFLUENCE THE CHILD COMPONENT
const [memberName, setMemberName] = useState('')
const [editingMemberName, setEditingMemberName] = useState(
members.map(() => false)
)
// Update member name
const editMemberName = async (_, index) => {
let new_editing_members_state = members.map(() => false)
new_editing_members_state[index] = true
setEditingMemberName(new_editing_members_state)
}
// Cancel editing mode
const cancelEditMemberName = async (_, index) => {
let new_editing_members_state = members.map(() => false)
new_editing_members_state[index] = false
setEditingMemberName(new_editing_members_state)
}
// UPDATE name in database
const updateMemberName = async (index, id) => {
let new_editing_members_state = members.map(() => false)
new_editing_members_state[index] = false
setEditingMemberName(new_editing_members_state)
}
// BELOW, LOOPS OVER EACH ITEM
const memberItems = members.map((member, index) => {
return (
<MemberItem
member={member}
index={index}
editingMemberName={editingMemberName[index]}
editMemberName={editMemberName}
handleChangeName={handleChangeName}
updateMemberName={updateMemberName}
cancelEditMemberName={cancelEditMemberName}
destroyMember={destroyMember}
/>
)
})
return (
// RENDER THE LIST OF ITEMS
{memberItems}
)
}
// Member.js (Child Component)
export const MemberItem = (
member,
index,
editingMemberName,
editMemberName,
handleChangeName,
updateMemberName,
cancelEditMemberName,
destroyMember
) => {
return (
<div
key={member.id}
>
<div>
{editingMemberName[index] ? (
<input
type="text"
placeholder="Johnny Appleseed"
onChange={handleChangeName}
/>
) : (
<>
<div>
{member.name.substring(0, 1).toUpperCase()}
</div>
<h3>{member.name}</h3>
</>
)}
</div>
<div>
{editingMemberName[index] ? (
<button
onClick={() => updateMemberName(index, member.id)}
>
<CgCheckO size=".75em" />
</button>
) : (
<button
onClick={() => editMemberName(member.id, index)}
>
<FiTool size=".75em" />
</button>
)}
<button>
{editingMemberName[index] ? (
<GiCancel
onClick={() => cancelEditMemberName(member.id, index)}
size=".75em"
/>
) : (
<RiDeleteBinLine
onClick={() => destroyMember(member.id)}
size=".75em"
/>
)}
</button>
</div>
</div>
)
}
Currently, I am getting an error of TypeError: editingMemberName is undefined and a warning of Each child in a list should have a unique "key" prop, but if you see, I do pass in an id into the key property.
In React, props are passed down to function components as a single object.
Your component function assumes props are passed down as separate arguments and not in a single object.
Fixed component definition (note the brackets around the argument list):
MemberItem = ({
member,
index,
editingMemberName,
editMemberName,
handleChangeName,
updateMemberName,
cancelEditMemberName,
destroyMember
}) => { ... }
This method of unpacking properties is called object destructuring.

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>
</>
);
}

Categories

Resources