Sort array onClick and render the array in React - javascript

I have an array full of objects that I fetched and I am trying to sort the array onClick of an arrow in React. I have a sort function that works perfect in javascript but I am new to React and can't figure out how to implement the function and render the list again.
I am getting errors messages of all sorts depending on what I try. Anything from cannot sort undefined to 'expecting onclick to be a function instead of an object like in this case.
var LeaderBoard = React.createClass({
sortDescending: function(property) {
return function (a,b) {
return (a[property] > b[property]) ? -1 : (a[property] < b[property]) ? 1 : 0;
}
},
getInitialState: function() {
return {
data: loading
};
},
componentWillMount: function() {
fetch('https://fcctop100.herokuapp.com/api/fccusers/top/recent', {
method: 'get'
}).then(response => response.json()).then(data => {
this.setState({
data: data,
});
}).catch(function(error) {
console.log("error is ", error);
});
},
render: function() {
var information = [];
for (var j=0; j<13; j++) {
information.push(
<div className="row" key={this.state.data.username}>
<div className="col-md-1 col-xs-1">
<h4>{j+1}</h4>
</div>
<div className="col-md-4 col-xs-4">
<h4>{this.state.data[j].username}</h4>
</div>
<div className="col-md-4 col-xs-4">
<h4>{this.state.data[j].recent}</h4>
</div>
<div className="col-md-3 col-xs-3">
<h4>{this.state.data[j].alltime}</h4>
</div>
</div>
);
}
return (
<div>
<div id="Title" className="row">
<h1>freeCodeCamp Leaderboard</h1>
</div>
<div className="row">
<div className="col-md-1 col-xs-1">
<h4>#</h4>
</div>
<div className="col-md-3 col-xs-3">
<h4>Camper Name
</h4>
</div>
<div className="col-md-5 col-xs-5">
<h4>Points in past 30 days
<img className="arrow" src="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-arrow-up-b-128.png" />
<img className="arrow" onClick = {this.state.data.sort(this.sortDescending("recent"))}
src="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-arrow-down-b-128.png" />
</h4>
</div>
<div className="col-md-3 col-xs-3">
<h4>All time points</h4>
</div>
</div>
<div>{information}</div>
</div>
);
}
});

You are setting onClick to tbe result of a function, not tbe function itself.
Replace
`<img className="arrow" onClick={this.state.data.sort(this.sortDescending("recent"))} src="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-arrow-down-b-128.png" />`
With
<img className="arrow" onClick = {() => this.state.data.sort(this.sortDescending("recent"))} src="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-arrow-down-b-128.png" />
React onClick events cannot be anything but a function, for future reference. (Also, JS refers to Arrays sometimes as Objects, which explains the error you're getting)

Related

Why my call map function is hide in the count nodeList but it is visible in return output?

This will be a complex sample cause my codes is like a lot...
So basically I just direct to my point. My problem is that I don't know why my list map function is not includes the output function of return when I'm trying to get the document.querySelectorAll() of all the items. Basically something like this.
<div className='gallery' style={ ispost ? { display:'none' } : {display:"block"}}>
{mainList.map((elem,idx) => {
return (
<div className="gallery-container" key={idx}>
<div className="l">
<div className="l-box">
<div className="text">
<div className="hide">
<h2> Onigsahima Tokyo </h2>
</div>
</div>
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
</div>
<div className="r">
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
</div>
</div>
)
})}
<div className="gallery-container" style={ ispost ? { display:'none' } : {display:"inline-grid"}}>
<div className="l">
<div className="l-box">
<div className="text">
<div className="hide">
<h2> Onigsahima Tokyo </h2>
</div>
</div>
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
</div>
<div className="r">
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
</div>
</div>
</div>
And then I have a useEffect() and I'll show all the codes about it..but don't get confused about it just focus on the querySelectorAll() cause it is the only thing that affect it all the animation
useEffect(() => {
gsap.registerPlugin(ScrollTrigger,CSSRulePlugin)
// gsap.registerPlugin(CSSRulePlugin)
const sections_ = document.querySelectorAll('.gallery-container')
console.log(sections_)
let sections = gsap.utils.toArray(".gallery-container")
console.log(sections)
let scrollTween = gsap.to(sections, {
xPercent: -100 * (sections.length - 1),
ease: "none", // <-- IMPORTANT!
scrollTrigger: {
trigger: ".gallery",
pin: true,
scrub: 3,
end: "+=3000",
// markers:true
}
});
gsap.to(".gallery-container:nth-child(1) .color-sample",{
y:0,
ease:"none",
scrollTrigger:{
trigger:".gallery-container:nth-child(1)",
containerAnimation:scrollTween,
start:"0% 0%",
end:"10% 6%",
// markers:{startColor: "orange", endColor: "green"},
scrub:2,
}
})
const rule = CSSRulePlugin.getRule(`.r-box:after`)
const rule2 = CSSRulePlugin.getRule(`.r-box:before`)
gsap.to([rule,rule2],{
width:'35%',
height:'35%',
background:"yellow",
delay:3,
ease:"none",
scrollTrigger:{
trigger:`.gallery-container:nth-child(1)`,
containerAnimation:scrollTween,
start:"0% 0%",
end:"10% 6%",
// markers:true,
scrub:2,
}
})
gsap.to(`.gallery-container:nth-child(1) .text h2`,{
y:0,
// delay:2,
ease:"none",
scrollTrigger:{
trigger:`.gallery-container:nth-child(1)`,
containerAnimation:scrollTween,
start:"0% 0%",
end:"10% 6%",
// markers:true,
scrub:2,
}
})
sections.forEach((elem,i) => {
gsap.to(`.gallery-container:nth-child(${i+2}) .color-sample`,{
y:0,
ease:"none",
scrollTrigger:{
trigger:`.gallery-container:nth-child(${i+2})`,
containerAnimation:scrollTween,
start:"center 50%",
end:"center 60%",
// markers:true,
scrub:2,
}
})
gsap.to(`.gallery-container:nth-child(${i+2}) .text h2`,{
y:0,
// delay:2,
ease:"none",
scrollTrigger:{
trigger:`.gallery-container:nth-child(${i+2})`,
containerAnimation:scrollTween,
start:"center 50%",
end:"center 60%",
// markers:true,
scrub:2,
}
})
const rule = CSSRulePlugin.getRule(`.r-box:after`)
const rule2 = CSSRulePlugin.getRule(`.r-box:before`)
gsap.to([rule,rule2],{
width:'35%',
height:'35%',
background:"yellow",
delay:3,
ease:"none",
scrollTrigger:{
trigger:`.gallery-container:nth-child(${i+2})`,
containerAnimation:scrollTween,
start:"center 50%",
end:"center 60%",
// markers:true,
scrub:2,
}
})
})
const arrowsColor = document.querySelectorAll('.arrow.first ion-icon')
const arrows2Color = document.querySelectorAll('.arrow.second ion-icon')
var scrollPos = 0
window.addEventListener('scroll',() => {
if ((document.body.getBoundingClientRect()).top > scrollPos) {
// console.log('Right')
arrowsColor.forEach(elem => {
elem.style.color = 'black'
})
arrows2Color.forEach(elem => {
elem.style.color = 'yellow'
})
} else {
// console.log('Left')
arrowsColor.forEach(elem => {
elem.style.color = 'yellow'
})
arrows2Color.forEach(elem => {
elem.style.color = 'black'
})
}
scrollPos = (document.body.getBoundingClientRect()).top
})
},[])
I don't know if this is easiest way to explain it but this is the best I can..
Note that mainList is a useState so I'll have something like this in my codes
when I am pushing the items in setMainList
useEffect(() => {
axios.get('http://localhost:7777/gallery')
.then(res => {
let del = []
res.data.map((elem,i) => {
console.log(i)
setMainList([...mainList,elem])
})
})
},[])
Yes it works but why the document.querySelectorAll don't detect if there is a new element that I pop in that mainList.map function? I don't understand why..It shouldn't be this kind of way. I always do this but never encounter this bug..
Note that mainList.map() is like visible but it is not include in that querySelectorlAll
Please help badly need it to get this thing done.
Because your useEffect callback runs only once since you haven't added any dependencies. If you want the callback to be invoked whenever mainList changes, add mainList to the useEffect dependency list like so
useEffect(() => {
//...
}, [mainList])
Be careful though! You should use the return callback to remove any listeners or subscriptions you've set up.
useEffect(() => {
//...
function onScroll() {
//...
}
window.addEventListener('scroll', onScroll)
return () => {
// remove listeners
window.removeEventListener('scroll', onScroll)
}
}, [mainList])
I don't know how gsap works, but you might also want to "deregister" the plugin in the tear-down callback, or just use a different useEffect callback with no dependencies to register it only once - you probably still want to "deregister" it either way, but you wont do it every time mainList changes.
Edit: I highly suggest you read the useEffect docs
Edit2: Here's a gist

Reactjs handleclick binding in a nested map not working

I am writing a simple blog component in React. The component get the post from a second file and renders the content and the hashtags with map (in particular two nested map). I was trying to create a part in which the hashtag are highlighted when clicked, and hence I was following the snippet of the answer to this question. The fact is that it is not working and in particular what it is not working is the binding through bind (checked with console.log output)
class Blog extends React.Component {
state= {
open: {}
}
handleClick = (k) => {
let linkOpenState = true;
if (this.state.open.hasOwnProperty(k)) {
linkOpenState = !this.state.open[k];
}
this.setState({ open: { [k]: linkOpenState } })
}
render(){
return(
posts.map(i=>(
<div class="box">
<article class="media">
<div class="media-content">
<div class="content">
<h1 class="title">{i.title}</h1>
<p>
{i.content}
</p>
</div>
<div class="tags">
{i.hash.map(k=>(<span id={k} onClick={this.handleClick.bind(this,k)} class={this.state.open[k]? "tag is-primary" : "tag"} >{k}</span>))}
</div>
</div>
</article>
</div>))
)
}
}
export default Blog
Anyone has an idea of what is wrong there? Is it the nested map a problem for the bind?
React expects a single element as a return. You can solve this by wrapping the return in a fragment as <React.Fragment> or <>.
Also, you don't need to bind an arrow function(handleClick when mapping i.hash).
render(){
return(
<>
posts.map(i=>(
<div class="box">
<article class="media">
<div class="media-content">
<div class="content">
<h1 class="title">{i.title}</h1>
<p>
{i.content}
</p>
</div>
<div class="tags">
{i.hash.map(k=>(<span key={k} id={k} onClick={() => this.handleClick(k)} class={this.state.open[k]? "tag is-primary" : "tag"} >{k}</span>))}
</div>
</div>
</article>
</div>))
</>)}}
You don't need to bind the arrow function.
Also, you need to pass unique key to elements when used inside map, you can use index as a key but if you are mutating the array then use some id or hash or anything which will be unique.
class Blog extends React.Component {
state= {
open: {}
}
handleClick = (k) => {
let linkOpenState = false;
if (this.state.open.hasOwnProperty(k)) {
linkOpenState = this.state.open[k];
}
this.setState({ open: { [k]: linkOpenState } })
}
render(){
return(
posts.map((i, index)=>(
<div class="box" key={i.id || index}>
<article class="media">
<div class="media-content">
<div class="content">
<h1 class="title">{i.title}</h1>
<p>
{i.content}
</p>
</div>
<div class="tags">
{i.hash.map(k=>(<span key={k} id={k} onClick={() => this.handleClick(k)} class={this.state.open[k]? "tag is-primary" : "tag"} >{k}</span>))}
</div>
</div>
</article>
</div>))
)
}
}
export default Blog

React Error: Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null

EDIT: [SOLVED by the community - in the answers!] Thanks everyone
I'm trying to add search functionalities to my webApp (a list of the movies I own). To do this, from the app I'm calling a functional component (MovieListFiltered) which has the following code:
MovieListFiltered.js:
import React from 'react'
const MovieListFiltered = (props) => {
const newData = props.moviesAfterFilter
if(newData !== null) {
const newMovies = newData.map((movie, i) =>
{
return(
<div className="col s12 m3 l3" key={i} movieid ={movie.idFromTmdb}>
<div className="card">
<div className="card-image waves-effect waves-block waves-light">
<img src={movie.url2poster} alt={movie.movieTitle} className="responsive-img" />
<p className="littleFont" align="center"><span><b>{movie.movieTitle}</b></span></p>
</div>
<div className="card-action">
<a href="#" onClick={() => this.props.viewMovieInfo(movie.idFromTmdb)}>Movie Details</a>
</div>
</div>
</div>
);
})
console.log(newMovies)
props.movieCallback(newData, newMovies);
} else {
return null
}
}
export default MovieListFiltered
So, basically, notything special there: you see many console.log calls, that was just to make sure the correct array of data was passed (and it is!)
In App.js:
... code not interesting goes here ...
callbackFromList = (childDataData, childDataMovies) => {
this.setState({moviesToFilter: childDataData});
this.setState({moviesToShow: childDataMovies});
this.setState({totalResults: childDataData.length});
}
render()
{
... some not interesting code goes here...
return(
<div className="App">
<Nav />
<div>
<div className="container">
<div className="row">
<div className="col s10 offset-s1">
<MovieListFiltered viewMovieInfo={this.viewMovieInfo} movieCallback={() => this.callbackFromList} ref={this.movieListRef} moviesAfterFilter={this.state.moviesFiltered}></MovieListFiltered>
</div>
</div>
</div>
</div>
</div>
);
}
Can you please help me? I've read all the questions already made here on stackoverflow, but nothing seems to fit to my case.
I think you are wanting something like this:
const MovieListFiltered = (props) => {
const newData = props.moviesAfterFilter
if(newData !== null) {
const newMovies = newData.map((movie, i) => (
<div className="col s12 m3 l3" key={i} movieid ={movie.idFromTmdb}>
<div className="card">
<div className="card-image waves-effect waves-block waves-light">
<img src={movie.url2poster} alt={movie.movieTitle}
className="responsive-img" />
<p className="littleFont" align="center"><span><b>
{movie.movieTitle}</b></span></p>
</div>
<div className="card-action">
<a href="#" onClick={() =>
this.props.viewMovieInfo(movie.idFromTmdb)}>Movie Details</a>
</div>
</div>
</div>
);
)
console.log(newMovies)
props.movieCallback(newData, newMovies)
return newMovies
} else {
return null
}
}
Here a cleaner version with only one return. That may not be what you're looking for though.
import React from 'react'
const MovieListFiltered = (props) => {
const newData = props.moviesAfterFilter || []; // add 'or' if null or undefined
const newMovies = newData.map((movie, i) => (
<div className="col s12 m3 l3" key={i} movieid ={movie.idFromTmdb}>
<div className="card">
<div className="card-image waves-effect waves-block waves-light">
<img src={movie.url2poster} alt={movie.movieTitle} className="responsive-img" />
<p className="littleFont" align="center"><span><b>{movie.movieTitle}</b></span></p>
</div>
<div className="card-action">
<a href="#" onClick={() => this.props.viewMovieInfo(movie.idFromTmdb)}>Movie Details</a>
</div>
</div>
</div>
));
console.log(newMovies)
props.movieCallback(newData, newMovies);
return newMovies;
}
export default MovieListFiltered
You are only returning a value from the else block. The if block is not currently returning anything. You probably want to make the last line of the if block return newMovies;
import React from 'react'
const MovieListFiltered = (props) => {
const newData = props.moviesAfterFilter
if(newData !== null) {
const newMovies = newData.map((movie, i) =>
{
return(
<div className="col s12 m3 l3" key={i} movieid ={movie.idFromTmdb}>
<div className="card">
<div className="card-image waves-effect waves-block waves-light">
<img src={movie.url2poster} alt={movie.movieTitle} className="responsive-img" />
<p className="littleFont" align="center"><span><b>{movie.movieTitle}</b></span></p>
</div>
<div className="card-action">
<a href="#" onClick={() => this.props.viewMovieInfo(movie.idFromTmdb)}>Movie Details</a>
</div>
</div>
</div>
);
});
console.log(newMovies);
props.movieCallback(newData, newMovies);
return newMovies;
}
return null;
}
export default MovieListFiltered
Also, you might notice I got rid of the entire else block - this is because it's not necessary if you return from the corresponding if block.

React.js+Bootstrap rendering rows

I am using bootstrap and I want to render 4 columns in row. However I do not know how many columns I will have, so it should work for any number of columns and also first column can be different react component then other columns.
What I want to achieve:
<div class="row">
<div class="col-md-3 component-type-1">...</div>
<div class="col-md-3 component-type-2">...</div>
<div class="col-md-3 component-type-2">...</div>
<div class="col-md-3 component-type-2">...</div>
</div>
<div class="row">
<div class="col-md-3 component-type-2">...</div>
<div class="col-md-3 component-type-2">...</div>
<div class="col-md-3 component-type-2">...</div>
<div class="col-md-3 component-type-2">...</div>
</div>
I tried something but did not managed to achieve what I want..
import chunk from 'lodash/chunk.js'
class Test extends React.Component {
render() {
let component1=null;
if (this.state.shouldBeComponent1) {
component1=<Component1 key="component1" />;
}
let items = [];
if (component1!=null) items.push(component1);
this.state.dataForComponents2.forEach((data) => {
items.push(<Component2 key={data.ID} />)
});
const rows = chunk(items, 4);
let pageBody=null;
if (items.length>0) {
pageBody=(
rows.map((row) => {
<div className="row">
{
row.map((item) => (
{item}
))
}
</div>})
);
}
return (
<div>
<div className="header">
///Other unreleated code
</div>
{pageBody}
</div>
);
}
Seems something wrong with your pageBody
Try this.
let pageBody= [];
if (items.length>0) {
rows.forEach((row, idx) => {
pageBody.push
(
<div key={idx} className="row">
{row}
</div>
)}
);
}
And example here

How to show loader in reactjs and meteor?

I have a page where 3 recipes are listed along with more button. When more button is clicked more 3 recipes are listed. What i am trying to do is before listing more 3 recipes i want to show a spinner/loader icon but i could only change the text of button. How can i show loader icon as soon as more button is clicked and before those additional 3 recipes are listed. I am using meteorjs and reactjs.
my code for this is
export default class Home extends Component {
constructor(){
super();
this.state = { limit:3 , loading:false }
this.addMore = this.addMore.bind(this);
}
getMeteorData(){
let data = {};
data.recipes = [];
data.recipes = Recipes.find({},{sort:{createdAt:-1}}).fetch();
let recipeHandle = Meteor.subscribe('recipelist',this.state.limit);
if(recipeHandle.ready()){
data.recipes = Recipes.find({},{sort:{createdAt:-1},limit:this.state.limit}).fetch();
}
return data;
}
addMore(){
this.setState({
limit:this.state.limit + 3, loading: true }, () => {
this.setTimeout(()=>{
this.setState({loading:false});
},2000);
});
}
render() {
console.log('this.data.recipes',this.data.recipes);
let recipes = _.map(this.data.recipes,(recipe) => {
return <RecipeList key={recipe._id} recipe={recipe} loading={this.state.loading} />
});
return (
<div className="row">
<div className="intro blink z-depth-1">
<div className="row">
<div className="col l7">
<h1 className="heading flow-text blink">Sell your Recipe</h1>
</div>
</div>
</div>
<div className="row">
<div className="col s12">
{recipes}
</div>
</div>
<button onClick={this.addMore} type="button" className="btn coral more">More</button>
</div>
);
}
}
ReactMixin(Home.prototype, ReactMeteorData);
You could remove the loading state, and just compute the loading state from the data: {this.state.limit !== this.data.recipes.length && <img src="path_to_loader.gif" />}
I am not sure where you want to show the loader, but for example you could do:
render() {
console.log('this.data.recipes',this.data.recipes);
let recipes = _.map(this.data.recipes,(recipe) => {
return <RecipeList key={recipe._id} recipe={recipe} loading={this.state.loading} />
});
return (
<div className="row">
<div className="intro blink z-depth-1">
<div className="row">
<div className="col l7">
<h1 className="heading flow-text blink">Sell your Recipe</h1>
</div>
</div>
</div>
<div className="row">
<div className="col s12">
{recipes}
</div>
</div>
{this.state.limit !== this.data.recipes.length && <img src="path_to_loader.gif" />}
<button onClick={this.addMore} type="button" className="btn coral more">More</button>
</div>
);
}

Categories