React key error although all elements have unique identifier - javascript

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

Related

ReactJS ..event handler works with one of the props but not all

I am trying to send id prop to a even handler to handle an event .I have a map function sending the name and id as props as below. When I Try to send the id to event handler in Delete Todo component I get id as undefined in console.log().But when I use the Name prop,console.log() works fine. Any idea what I am actually missing. Please help.
const todolistt=todolist.map(td=>{
return (
<div>
<Deletetodo todo={td.id} name={td.name} />
</div>
)
})
const handleDelete=(id)=>{
console.log(id)//Getting undefined
}
const Deletetodo = ({id,name}) => {
return (
<div>
<h1>{name}</h1>
<p onClick={()=>{handleDelete(id)}}>X</p>
</div>
)
}
const handleDelete=(name)=>{
console.log(name)//Name appers
}
const Deletetodo = ({id,name}) => {
return (
<div>
<h1>{name}</h1>
<p onClick={()=>{handleDelete(name)}}>X</p>
</div>
)
}
You need to name the prop "id" like this:
<Deletetodo id={td.id} name={td.name} />
You're passing in a prop called "todo" but looking for a prop called "id"

How to use onClick event on Card.js component to render Article.js Component in React?

Right now i am in Home.js page and i want to render Article.js component/page when user click on particular card (Card.js component). Here is my Home.js code
const Home = () => {
const posts = useSelector((state) => state.posts)
const [currentId, setCurrentId] = useState(null)
const handleClick = () => {
return <Article />
}
return (
<div className="container">
<h4 className="page-heading">LATEST</h4>
<div className="card-container">
{
posts.map(post => <Card key={post._id} post={post} setCurrentId={setCurrentId} onClick={handleClick} />)
}
</div>
</div>
)
}
ONE MORE PROBLEM :
How can I send post variable into onClick method? when i send it method is getting called.
Thank You in Advance :)
It sounds like you want to use the React Router? As I take it you want to load the post as its own page?
I should also point out that any function passed to onClick cannot return anything. The only purpose return can serve in an event function is to exit the function early.
I do agree with #Jackson that you might want to to look into React Router. But you don't need it. You can conditionally render the Article component based on the currentId.
A click handler shouldn't return anything. Instead of returning the <Article /> from the onClick callback, you would use onClick to control the currentId state. You can pass a function that sets the currentId to the post id based on the post variable in your map like this: onClick={() => setCurrentId(post._id)}.
The return for your Home component will either render the list of posts or a current post, depending on whether or not you have a currentId or just null.
const Home = () => {
const posts = useSelector((state) => state.posts);
const [currentId, setCurrentId] = useState(null);
return (
<div className="container">
{currentId === null ? (
// content for list of posts - when currentId is null
<>
<h4 className="page-heading">LATEST</h4>
<div className="card-container">
{posts.map((post) => (
<Card
key={post._id}
post={post}
// arrow function takes no arguments but calls `setCurrentId` with this post's id
onClick={() => setCurrentId(post._id)}
/>
))}
</div>
</>
) : (
// content for a single post - when currentId has a value
<>
<div
// setting currentId to null exits the aritcle view
onClick={() => setCurrentId(null)}
>
Back
</div>
<Article
// could pass the whole post
post={posts.find((post) => post._id === currentId)}
// or could just pass the id and `useSelector` in the Article component to select the post from redux
id={currentId}
// can pass a close callback to the component so it can implement its own Back button
onClickBack={() => setCurrentId(null)}
/>
</>
)}
</div>
);
};
To pass in the click hadler the params you want, one could do something like this:
posts.map(post =>
<Card
key={post._id}
post={post}
onClick={() => handleClick(post)} />
)

React Warning: Each child in a list should have a unique "key" prop. in render() function [duplicate]

This question already has answers here:
Understanding unique keys for array children in React.js
(27 answers)
Closed 2 years ago.
I am calling an API endpoint, saving it's data to a state and then rendering it. It's displaying in the browser but there is a warning on the console: Warning: Each child in a list should have a unique "key" prop..
My app.js:
class App extends Component {
render () {
return (
<div>
<Profile profiles={this.state.profile} />
</div>
)
}
state = {
profile: []
};
componentDidMount() {
fetch('http://127.0.0.1:8000/profiles')
.then(res => res.json())
.then((data) => {
this.setState({ profile : data })
})
.catch(console.log)
}
}
export default App;
I don't understand where do I put the key prop in render(). This is my snippet profile.js:
const Profile = ({ profiles }) => {
return (
<div>
<center><h1>Profiles List</h1></center>
{profiles.map((profile) => (
<div className="card">
<div className="card-body">
<h5 className="card-title">{profile.first_name} {profile.last_name}</h5>
<h6 className="card-subtitle mb-2 text-muted">{profile.dob}</h6>
<p className="card-text">{profile.sex}</p>
</div>
</div>
))};
</div>
)
};
export default Profile;
What improvement do the key prop brings over not using it? I am getting overwhelmed with these <div>...</div> tags.
If you use map in your JSX return then you need to provide the parent element with the key prop so that it's uniquely identified.
https://reactjs.org/docs/lists-and-keys.html
You would preferably use an object ID but if you know a field (or combinations of fields) that would constitute a unique key then you could use that instead:
{profiles.map((profile) => (
<div
key={'profileList_'+profile.first_name+profile.last_name}
className="card"
>
...
</div>
)};
NOTE: In the example I used profileList_ as a prefix just in case you need to use the same unique identifier (object ID or in this case profile.list_name+profile.last_name) as a key somewhere else in a different context.
you have to set uniq value to key props to the first div inside map
{profiles.map((profile) => (
<div key={profile.id} className="card">
read the doc

How to pass props to subcomponent in Reactjs

I am attempting to pass props from a parent component function to a child in React without any luck. I am utilizing the React Data Table Component's filtering functionality. The example documentation in Storybook is great, however I want to change the search box into a list of tags, that when clicked, will filter the data table just the same.
The data I'm trying to parse into individual "tags" is structured like this:
[{"id":"09090","first_name":"Cynthia","last_name":"McDonald","email":"email1#gmail.com","profile_url":"https:myprofile.com/1","types":["professor","science"]},
{"id":"03030","first_name":"Ryan","last_name":"Burke","email":"email2#gmail.com","profile_url":"https://myprofile.com/2","types":["student","science"]},
{"id":"05050","first_name":"Stewart","last_name":"Hook","email":"email3#gmail.com","profile_url":"https://myprofile.com/3","types":["professor","math"]}]
I am trying to create a unique tag list of the "types" attribute that acts as a filter onClick instead of onChange, just like the original search textbox example. So based on the sample data, we would end up with tags for professor, science, student, and math.
If you look at the code in Storybook, more specifically, line 45:
const subHeaderComponentMemo = React.useMemo(() =>
<Filter onFilter={value => setFilterText(value)} />, []);
My data is loaded via an API call and I am trying to pass that data to the subHeaderComponentMemo with props, i.e.,
const subHeaderComponentMemo = React.useMemo(() =>
<Filter data={people} onFilter={value => setFilterText(value)} />, []);
I am then trying to receive and loop through that data on line 20 and am replacing most of that code so that it will render the unique tags from the types attribute of the data.
Storybook code:
const Filter = ({ onFilter }) => (
<TextField id="search" type="search" role="search" placeholder="Search Title" onChange={e => onFilter(e.target.value)} />
);
My failed code
const Filter = ({props, onFilter }) => {
// Get a unique array of types
const types = [...new Set(props.types.map(type => type))];
return types.map(type => (
<span
className="badge badge-primary"
onClick={() => onFilter({ type })}
onKeyDown={() => onFilter({ type })}
tabIndex="0"
role="button"
>
{type}
</span>
));
};
This is of course resulting in an epic fail. The module isn't even displaying.
I'm new to React, so any insight/help would be greatly appreciated.
Updated code based on feedback
const Filter = ({ data, onFilter }) => {
console.dir(data);
if (data.length === 0) {
return <div>No data</div>;
}
const types = [...new Set(data.types.map(type => type))];
return (
<>
{types.map(type => (
<span
className="badge badge-primary"
onClick={() => onFilter({ type })}
onKeyDown={() => onFilter({ type })}
tabIndex="0"
role="button"
>
{type}
</span>
))}
;
</>
);
};
One of the errors I got when I made the changes was "cannot read property of 'map' undefined", which I equated to the fact that this component may be rendering before the data gets there and it is empty. So I added an if with a return in case this happens.
I also rewrote the return the statement in Filter based on the feedback because it did look like it would loop and try to render a new component for each iteration.
However, the issue still stands with the data not being present. The table is now loading with the data, but this component is just returning the "No data" div as if nothing ever happened. I'm not sure if this is due to the useMemo hook or what. Besides that, no errors.
Thank you for your time and feedback.
Another update
UseMemo needed the prop to be a dependency in its array... I'm finally seeing data when I console.log it.
const subHeaderComponentMemo = useMemo(
() => <Filter data={people} onFilter={value => setFilterText(value)} />,
[people]
);
However, I'm getting the 'map' undefined error again, as if my data is not in the structure I expect it to be in. I have not changed it and this is the code I'm using to try to access it (as shared before):
const types = [...new Set(data.types.map(type => type))];
Thanks again everyone... getting closer!
you should rewrite you component signature to match the one you are trying to use
const Filter = ({props, onFilter }) => // you are expecting a props name props
<Filter data={people} onFilter={value => setFilterText(value)} />, []); // you are passing data and setFilterText
so you should change it to
const Filter = ({data, onFilter }) =>

How to change State from inside .Map function React

I have this function
renderCompanies() {
if (this.props.companies)
return [
<div>
Dashboard hello <div>{this.renderProfile()}</div>
<div>
{this.props.companies.map(function(item, i) {
return (
<div>
<div
key={i}
onClick={item => {
this.setState({ currentCompany: item });
}}
>
{i}: {item.name}
</div>
<button>Delete Company</button>
</div>
);
})}
</div>
<AddCompanyPopUp />
</div>
];
}
I want to loop though this.props.companies and render a list of items. I want a user to be able to click on a specific item and have the item be saved to state.
This function runs inside another funtion
renderEitherMenuOrCompanyList() {
if (this.state.currentCompany) {
return <Menu companies={this.state.currentCompany} />;
} else {
return <div>{this.renderCompanies()}</div>;
}
}
Both are already bound to this
this.renderCompanies = this.renderCompanies.bind(this);
this.renderProfile = this.renderProfile.bind(this);
this.renderEitherMenuOrCompanyList = this.renderEitherMenuOrCompanyList.bind(this)
The renderEitherMenuOrCompanyList function is being called inside the render react function/method.
My problem is that I cannot set the state from the renderCompanies .map function. I keep getting "Cannot read property 'setState' of undefined" . This should be simple but I have not been able to do it
Make sure the function given to map is bound as well, or an arrow function:
{this.props.companies.map((item, i) => {
return (
<div>
<div
key={i}
onClick={() => {
this.setState({ currentCompany: item });
}}
>
{i}: {item.name}
</div>
<button>Delete Company</button>
</div>
);
})}
The function passed to this.props.companies.map isn’t an arrow function, so it creates a new this. Change it to an arrow function to preserve the this from outside of it.
this.props.companies.map( ( item, i ) => { ... } )
You’ve also named the argument to onClick item, but it’s actually the click event. You want the item already defined by the map function. Name the argument to onClick something else, or nothing, to avoid overwriting the item variable you actually want.
onClick={ () => { ... } }

Categories

Resources