List is not rendering - arrays

The error I get:
Objects are not valid as a React child, If you meant to render a collection of children, use an array instead.
here res is a json which has nested arrays so i have used _.foreach for extracting it and state is successfully updating but problem lies in displaying it
class ViewExchange extends React.Component{
state={list:[],refresh:false}
componentWillMount(props){
if(_.isEmpty(Cookies.get())){
this.props.history.push("/signup")
}
else{
let platform = Cookies.get('platform')
axios.post('http://localhost:3001/user/viewexchange',{platform})
.then(res=>{
this.setState({list:res.data})
_.forEach(this.state.list,(value)=>{
_.forEach(value.url,(e)=>this.setState({list:[e]}))
})
})
}
}
renderList=()=>{
console.log("in method")
return this.state.list.map((item,key)=>{
return <div key={key}>{item}</div>
})
}
render(){
return (
<div>
{this.renderList()}
</div>
);
}
}
export default withRouter(ViewExchange);

This error mostly comes when you try to show some content in jsx which is not a valid object key ( instead is an object itself ). In your case it will be like:
return this.state.list.map((item,key)=>{ return <div key={key}>{item.index}</dv> }); // instead of item, you have to use item.index which is an existing key of object
Here item.index is the index which you want to show from the object and don't forget to provide the key, in absence of key it will show a warning also performance is compromised.

Related

How to throw an error in this.state.forEach statement in react

I read a bit about error throwing but I don't really understand how to throw an error for the specific code in this code below when react complains about that it is undefined.
Do I use the componentDidCheck and if yes how do you exactly do it? The code where it catches the error is below and it is the forEach where it blows up
class Subject extends React.Component {
state = {
subject: [],
}
render() {
var products = [];
var index = 0;
this.state.subject.forEach(product => {
subjects.push(<Product
key={index} subjectName = {this.state.subjects[index].name}
subjectPrice = {this.state.subject[index].salePrice}
subjecttId = {this.state.subject[index].itemId}
/>)
index++;
});
You are uneccessarily making complex logic. You have to utilize .map in your case. It’s just simple with .map
Don’t use index as key instead use itemId from each product as key. Index should be always second option if you don’t have unique id in the data then go for index otherwise don’t.
Since you are already doing .forEach or .map you no need to use index to get product details, because .map or .forEach will give you each object from products array. You can just access product.itemId and others like that
The reason you get undefined error may be because the products state is initially undefined or empty array and that’s why you get that error
So do check whether products is an array and contains list of objects by checking its length and then do .map
You no need to track index, you no need local array variable to push Product component into it
Below is the most efficient way of rendering Product component in simple and clean way
.map without return
render() {
const { products } = this.state;
return(
<div>
{Array.isArray(products) && products.length > 0 && products.map(product=> (
<Product key={product.itemId} productName = {product.name} productPrice = {product.salePrice} productImage = {product.thumbnailImage} productId = {product.itemId}
/>))}
</div>
)
}
.map with return
.map without return
render() {
const { products } = this.state;
return(
<div>
{Array.isArray(products) && products.length > 0 && products.map(product=> {
return <Product key={product.itemId} productName = {product.name} productPrice = {product.salePrice} productImage = {product.thumbnailImage} productId = {product.itemId}
/>})}
</div>
)
}
If you do like above you won’t get into errors. I posted this answer to correct you because you are making things complex
You can check error and info that componentDidCatch gives you and check these two return fallback UI accordingly

Render some text if array.filter returns 0 in React?

I have a filter on an array in the render function in a React component:
someArray.filter(item => {
if (item.name.includes(searchText))return true
}).map(item=>{
return <h1>{item.name}</h1>
});
How can I elegantly display some text along the lines of "No search results" when no items are being returned by the map function?
There are a few ways you can do this. You can use a ternary operator (and also shorten your callbacks):
const filtered = someArray.filter(item =>
item.name.includes(searchText)
);
//Then, in your JSX:
{
filtered.length > 0 ?
filtered.map((item, key) =>
<h1 key={key}>{item.name}</h1>
)
:
<h1>No search results</h1>
}
This checks if there are any filtered results. If so, it will map them to h1s that have the name of the item. If not, then it will simply render a single h1 with the text 'No search results'.
One possible way is, instead of putting this code directly inside JSX render method, put it inside a method and call that method from render.
Like this:
_filterItem(){
const arr = someArray.filter(item => item.name.includes(searchText))
if(!arr.length) return <div>No data found</div>;
return arr.map(item => <h1 key={/*some unique value*/}>{item.name}</h1>)
}
render(){
return(
<div>{this._filterItem()}</div>
)
}
Suggestion:
With filter and map you can use concise body of arrow function instead of block body, for more details check MDN Doc.
Short and concise:
someArray.map(({name}) => name.includes(searchText) && <h1>{name}</h1>)

React - Not able to update list of product component

I made a list of product which contains name, price etc.. properties. Then I created a simple search box and I am searching my products based on product name . the search result returns the correct object but the UI is not getting updated with that result.Initially, I am able to see the list but after searching it is not getting updated. I am new to react SO need some help. here is my code
OnInputChange(term)
{
let result= this.products.filter(product=>{
return product.name==term;
});
console.log(result);
let list=result.map((product)=>
{
return <li key={product.price}>{product.name}</li>
});
console.log(list);
this.setState({listOfProducts:list});
}
render()
{
this.state.listOfProducts=this.products.map((product)=>
{
return <li key={product.price}>{product.name}</li>
});
return <div>
<input onChange={event=>{this.OnInputChange(event.target.value)}}/>
<ul>{this.state.listOfProducts}</ul>
</div>
}
}`
this.products.filter should probably be this.state.products.filter
When referring to a Components method you say this.onInputChange but when referencing state you must say this.state.products because this.products doesn't exist.
I would also move this:
let list=result.map((product)=>
{
return <li key={product.price}>{product.name}</li>
});
to your render statement. so your onchange handler could be:
OnInputChange(term)
{
let result= this.products.filter(product=>{
return product.name==term;
});
this.setState({listOfProducts:result});
}
and then you
render(){
return(
{this.state.listOfProducts.map(product => {
return <li key={product.price}>{product.name}</li>
})}
)
}
hope that helps! If your problem persists please share your entire code so I can better understand the issue.

Reactjs - Need to bind a onClick handler twice?

Coming from Vue.js (two way data flow) I have question about react one way data flow - I have a Parent that have a handler for its child onClick:
<div className="recipe-container">
<button onClick={this.toggleRecipeList.bind(this)}>Show Recipes</button>
<RecipeList showRecipe={this.showRecipe} list={this.state.recipes} />
</div>
So, I pass showRecipe handler, which has only one parameter (and simply just logs it to the console).
My RecipeList looks like this (stateless func):
return (<ul className='recipe-list-bottom'>
{
props.list.map((rec, key) => {
return <li onClick={props.showRecipe(rec)} key={key}>{rec.title}</li>
})
}
</ul>)
I tried to launch showRecipe(rec) to have the current rec object as argument. Although I recive what I want, the handler is being fired from a button which is a sibling of RecipeList.
I manage to get it working by adding onClick={props.showRecipe.bind(null,rec)} to li element, but I find it really dirty way to do so.
Am I missing something? I thought showRecipe(rec) would be enough to get what I wanted. Why showRecipe(rec) is being fired with this set to button?
I think that your second snippet has a classic error:
return (<ul className='recipe-list-bottom'>
{
props.list.map((rec, key) => {
return <li onClick={props.showRecipe(rec)/*here*/} key={key}>{rec.title}</li>
})
}
</ul>)
You are assigning the result of calling showRecipe to the onClick parameter, not the function itself. The bind solutions works, but if you want to pass the parameter rec without using bind you need to wrap the call:
return (<ul className='recipe-list-bottom'>
{
props.list.map((rec, key) => {
return <li onClick={()=>props.showRecipe(rec)} key={key}>{rec.title}</li>
})
}
</ul>)
You can use es2015 stage-0 syntax in order to write it like this:
class Blah extends Component {
onClick = (recipe) => e => this.props.showRecipe(recipe);
render() {
return (
<ul className='recipe-list-bottom'>
{
props.list.map((rec, key) => {
return <li onClick={onClick(rec)} key={key}>{rec.title}</li>
})
}
</ul>
)
}
}

Display list of React Components into a table

I have a React component called Widget and I want to display a list of widgets as a table.
Let's say the array of widgets looks like A, B, C, D, E, F, G and I will allow up to 3 TD per Table Row. So the result will look like the following.
A B C
D E F
G
Although the simple line skip can be done with css by using Ul but I have to us e a table for header labels.
I tried making the original array into list of arrays like [[A,B,C], [D,E,F], [G]]:
{
chunkedPartsArray.map((chunk, ind) => {
return (<tr>
{
chunk.map((obj, ind) => {
return {obj}
})
}
</tr>)
})
}
The above code gives me the following warning
Uncaught Error: Objects are not valid as a React child (found: object with
keys {obj}). If you meant to render a collection of children, use an array
instead or wrap the object using createFragment(object) from the React add-
ons. Check the render method of WidgetsTab.
How can I solve this?
You should wrap your widgets within a td element. I have shown a demonstration using characters.
class App extends React.Component {
render() {
var chunkedPartsArray = [['A','B','C'], ['D','E','F'], ['G']]
return (
<tbody>{chunkedPartsArray.map((chunk, ind) => {
console.log(chunk);
return (<tr>
{
chunk.map((obj, ind) => {
console.log(obj);
return <td>{obj}</td>
})
}
</tr>)
})
}</tbody>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react-dom.js"></script>
<div id="app"></div>
You can create a function out there and return html.
in react we need wrap the component into a div if it is more than one.
so you need a placeholder for child and return from function.
//in a file after imports
function getChunkedParts(chunk, ind){
function getChunks(obj, ind){return (<td>{obj}</td>)}
var chunks =chunk.map((obj, ind) =>getChunks(obj, ind));
return (<tr>{chunks} </tr>)
}}
//parent render
render{
var ChunkedParts = chunkedPartsArray.map((chunk, ind) =>getChunkedParts(chunk, ind);
return(<table>{ChunkedParts }</table>)
}

Resources