I am trying to display the product getting the size it should be from a Json database. I am new to react so have tried a few ways and this is what I have been able to do.
I tried making a function (FontSize) that creates a variable (percentage) with the value I want before and then tried calling the function in the render in the tag with the product. I am getting no errors but the size of the paragraph tag is not changing.
This is my component.
import React, { Component } from 'react';
import { Loading } from './LoadingComponent';
const API = 'http://localhost:3000/products';
class Products extends Component {
constructor(props) {
super(props);
this.state = {
products: [],
isLoading: false,
error: null,
};
}
componentDidMount() {
this.setState({ isLoading: true });
fetch(API)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong ...');
}
})
.then(data => this.setState({ products: data, isLoading: false }))
.catch(error => this.setState({ error, isLoading: false }));
}
FontSize = () => {
const { products } = this.state;
var percentage = products.size + 'px';
return percentage;
}
render() {
const Prods = () => {
return (
<div>
<div className="row">
<button onClick={this.sortPrice}>sort by price lower to higher</button>
<button onClick={this.sortSize}>sort by size small to big</button>
<button onClick={this.sortId}>sort by Id</button>
</div>
{products.map(product =>
<div className="row">
<div className="col-3">
<p> Price: ${(product.price/100).toFixed(2)}</p>
</div>
<div className="col-3">
<p style={{fontSize : this.FontSize()}} > {product.face}</p>
</div>
<div className="col-3">
<p>Date: {product.date} {this.time_ago}</p>
</div>
</div>
)}
<p>"~END OF CATALOG~"</p>
</div>
);
};
const { products, isLoading, error } = this.state;
if (error) {
return <p>{error.message}</p>;
}
if (isLoading) {
return <Loading />;
}
return (
<Prods />
);
}
}
export default Products;
What I get in the console using console.log(products)
I think you need quotes around your style value to work properly.
With concatenation it would look like this for Example:
style={{gridTemplateRows: "repeat(" + artist.gallery_images.length + ", 100px)"}}
Another general example from React:
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')',
};
function HelloWorldComponent() {
return <div style={divStyle}>Hello World!</div>;
}
Related
I just started to learn to react and now I'm stuck and hope for your help.
I am currently trying to limit my data from the news api and add a load-more-button to load more data.
I have found a good example here, but I can't manage to adapt it to my code.
Example: codepen.io
I hope you can help me.
My Code:
class App extends React.Component {
state = {
articles: [],
isLoading: true,
errors: null,
visible: 2
};
loadMore() {
this.setState(prev => {
return { visible: prev.visible + 4 };
});
}
getArticles() {
axios
.get(
"https://newsapi.org/v2/everything?q=ai&pageSize=100&sortBy=popularity&apiKey=API_KEY"
)
.then(response =>
response.data.articles.map(article => ({
date: `${article.publishedAt}`,
title: `${article.title}`,
url: `${article.url}`
}))
)
.then(articles => {
this.setState({
articles,
isLoading: false
});
})
.catch(error => this.setState({ error, isLoading: false }));
}
componentDidMount() {
this.getArticles();
}
render() {
const { isLoading, articles, visible } = this.state;
return (
<React.Fragment>
<H1>#AI</H1>
<div>
{!isLoading ? (
articles.map(article => {
const { date, title, url } = article;
return (
<Div key={url}>
<Div inner>
<P>{moment.utc(date).format("YYYY-MM-DD")}</P>
<A href={url}>{title}</A>
</Div>
</Div>
);
})
) : (
<Div loader />
)}
</div>
{this.state.visible < this.state.articles.length && (
<Button onClick={this.loadMore}>Load more</Button>
)}
</React.Fragment>
);
}
}
export default App;
what is missing that you didn't apply the visible count to the items before it gets mapped
{articles.slice(0, visible).map((article, index) => {
const { date, title, url } = article;
return (
<Div key={url}>
<Div inner>
<P>{moment.utc(date).format("YYYY-MM-DD")}</P>
<A href={url}>{title}</A>
</Div>
</Div>
);
})}
I would like to get value from a child component (DropDown) and display them in a parent class (App),
I explain, I have a drop-down list that is imported into the App class, when I choose a value in this drop-down list I modify my data that is displayed in my class App,
class App :
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
station: [],
stationValue: ''
}
}
getParking = async () => {
try {
const reponse = await axios.get(URL + "station/");
this.setState({
station: reponse.data['hydra:member']
});
} catch (e) {
console.log(e)
}
};
getData = async () => {
try {
const response = await axios.get(URL + "events?station=station_id");
this.setState({
data: response.data["hydra:member"]
});
} catch (error) {
console.log(error)
}
};
componentDidMount() {
this.getData();
this.getParking()
setInterval(this.appendData, 1000)
}
render() {
const {data, station} = this.state;
return (
<div>
<header>
<Dropdown dataStation={station}/>
</header>
{
data.map((item, key) =>
<div key={key}>
<>
{item.label}
</>
</div>
)
}
</div>
)
}
}
composent DropDown :
const Dropdown = ({dataStation}) => {
const [showMenu, setShowMenu] = useState(false);
const [selectItem, setSelectItem] = useState(showMenu);
const showList = () => {
setShowMenu(!showMenu)
};
const toggleSelected = (list) => {
setSelectItem(list.name);
setShowMenu(false)
};
return (
<>
<div className="dropdown-list-style" onClick={showList}>
<div style={{display: 'inline'}}>
{showMenu
? (<div style={{textAlign: 'right'}}><ChevronUp/></div>)
: (<div style={{textAlign: 'right'}}><ChevronDown/></div>)
}
{selectItem}
</div>
</div>
<div className="dropdown-list-style" style={{display: showMenu ? 'block' : 'none'}}>
{
dataStation.map((list, index) =>
<div key={index} onClick={() => toggleSelected(list)}>
{list.name}
</div>
)
}
</div>
</>
)};
So when I choose for example in the drop-down list the value "A", I will have to display the elements which are in "A", and "A" for example has a Id "1", and this id I will have to recover it and put it in my function (getData) which is in the class A. my code works when I put values written by hand for example when I put directly 1 in place of (station_id), but not when I wish to retrieve the id via the drop-down list.
Can you help me please?
inside your app component make callback that set the station value
setStation=(stationValue)=>{
this.setState({stationValue:stationValue})
}
passit to dropdown like this
<Dropdown dataStation={station} setStation={this.setStation}/>
inside dorpdow compoenent
uset it on click of item like:
{
dataStation.map((list, index) =>
<div key={index} onClick={() =>{toggleSelected(list); props.setStation(list)}}>
{list.name}
</div>
)
}
EDIT
you can use Station value in getData url like
getData = async () => {
try {
const response = await axios.get(URL + "events?station="+this.state.stationValue);
this.setState({
data: response.data["hydra:member"]
});
} catch (error) {
console.log(error)
}
};
I have a problem with my DropDown list with react, i want pre select a value in the list but i dont know how i can do it.
For exemple: before i select a value in the list, i want when get one before i select a value, for exemple the first element i get in my database.
class App :
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
station: [],
stationValue: ''
}
}
getParking = async () => {
try {
const reponse = await axios.get(URL + "station/");
this.setState({
station: reponse.data['hydra:member']
});
} catch (e) {
console.log(e)
}
};
getData = async () => {
try {
const response = await axios.get(URL + "events?station=" + this.state.stationValue);
this.setState({
data: response.data["hydra:member"]
});
} catch (error) {
console.log(error)
}
};
componentDidMount() {
this.getData();
this.getParking()
setInterval(this.appendData, 1000)
}
setStation=(stationValue)=>{
this.setState({stationValue:stationValue})
}
render() {
const {data, station} = this.state;
return (
<div>
<header>
<Dropdown dataStation={station} setStation={this.setStation} value={this.handleChange}/>
</header>
{
data.map((item, key) =>
<div key={key}>
<>
{item.label}
</>
</div>
)
}
</div>
)
}
}
composent DropDown :
const Dropdown = ({dataStation, setParking, value}) => {
const [showMenu, setShowMenu] = useState(false);
const [selectItem, setSelectItem] = useState(showMenu);
const showList = () => {
setShowMenu(!showMenu)
};
const toggleSelected = (list) => {
setSelectItem(list.name);
setShowMenu(false)
};
return (
<>
<div className="dropdown-list-style" onClick={showList}>
<div style={{display: 'inline'}}>
{showMenu
? (<div style={{textAlign: 'right'}}><ChevronUp/></div>)
: (<div style={{textAlign: 'right'}}><ChevronDown/></div>)
}
{selectItem}
</div>
</div>
<div className="dropdown-list-style" style={{display: showMenu ? 'block' : 'none'}}>
{
dataStation.map((list, index) =>
<div key={index} onClick={() => toggleSelected(list); props.setStation(list)}}>
{list.name}
</div>
)
}
</div>
</>
)};
i tried something like
dataStation[0].name
but its not good, someone can help me please?
You can use useEffect hook. When the get request for the stations finishes and props change, it will select the first element from the array as the default value.
useEffect(() => {
if (Array.isArray(dataStation) && dataStation[0]) {
selectItem(dataStation[0].name);
}
}, [dataStation]);
Trying to fetch the Jsonplaceholder users name and id and filter them in in the render method. I'm getting this error:
TypeError: this.state.robots.filter is not a function
class App extends React.Component {
constructor() {
super()
this.state = {
robots: [],
searchfield: ''
}
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => {
return response.json;
})
.then((users) => {
this.setState({robots: users});
})
}
onSearchChange = (event) => {
this.setState({searchfield: event.target.value});
}
render() {
const filteredRobots = this.state.robots.filter(robot => {
return robot.name.toLowerCase().includes(this.state.searchfield.toLowerCase());
})
return (
<div className="container text-center mt-4">
<h1 className="custom mb-5">RoboFriends</h1>
<SearchBox searchChange={this.onSearchChange} />
<CardList robots={filteredRobots} />
</div>
);
}
}
Could anyone give me a clue how to solve the problem? Thank you in advance!
You have to use .json() not json.
class App extends React.Component {
constructor() {
super()
this.state = {
robots: [],
searchfield: ''
}
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => {
return response.json();
})
.then((users) => {
this.setState({robots: users});
})
}
onSearchChange = (event) => {
this.setState({searchfield: event.target.value});
}
render() {
const filteredRobots = this.state.robots.filter(robot => {
return robot.name.toLowerCase().includes(this.state.searchfield.toLowerCase());
})
return (
<div className="container text-center mt-4">
<h1 className="custom mb-5">RoboFriends</h1>
<input onChange={this.onSearchChange} />
<pre>{JSON.stringify(filteredRobots, null, 2)}</pre>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I solved the problem by appending () to response.json:
return response.json();
Check the data type of users below: You are reassigning robots object with users and which should be array, if you want to access filter method. I think you data type of users i not array and that is why its throwing error. try printing users in console.
.then((users) => {
console.log(users);
this.setState({robots: users});
})
and correct below json() method in your code.
return response.json();
you should use fetch rest calling like that
fetch("https://jsonplaceholder.typicode.com/users").then(res => {
res.json().then(users => {
this.setState({ robots: users });
});
});
It should work.
response.json is a promise so you have to call with () something like this.
import React from 'react';
class App extends React.Component {
constructor() {
super()
this.state = {
robots: [],
searchfield: ''
}
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users').then(response => {
return response.json();
}).then((users) => {
this.setState({ robots: users });
})
}
onSearchChange = (event) => {
this.setState({ searchfield: event.target.value });
}
render() {
const filteredRobots = this.state.robots.filter(robot => {
return robot.name.toLowerCase().includes(this.state.searchfield.toLowerCase());
})
return (
<div className="container text-center mt-4">
<h1 className="custom mb-5">RoboFriends</h1>
<SearchBox searchChange={this.onSearchChange} />
<CardList robots={filteredRobots} />
</div>
);
}
}
export default App;
refer this link : https://www.robinwieruch.de/react-fetching-data
I have blogposts that I need to render. The first 4 are shown. When clicking on the button underneath it, two more need to show up. When the button is clicked again, two more need to show up and so on.
Unfortunately, I am not able to do so.
Here is my code:
import React from 'react';
import axios from 'axios';
import Blogpost from './Blogpost.js';
class BlogpostReader extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
blogposts: [],
};
}
componentDidMount() {
// Loading all blogposts in state
}
renderBlogpost(i) {
// Render one blogpost
}
//This function has to be replaced by one that renders extra blogposts
showAlert(){
alert("Im an alert");
}
render() {
const {error, isLoaded} = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
for (let i = 1; i < this.state.blogposts.length && i < 5; i++) {
this.state.renderedBlogposts.push(
<div key={this.state.blogposts[i].id} className="col-12 col-sm-12 col-md-12 col-lg-6 col-xl-6 whole-blogpost">
{this.renderBlogpost(this.state.blogposts[i])}
</div>)
}
return (
<div>
<div className="row">
{this.state.renderedBlogposts}
</div>
<div className="centered-button">
<button className="styled-button" onClick={this.showAlert}>Meer laden</button>
</div>
</div>
);
}
}
}
export default BlogpostReader;
How can I show extra blogposts after clicking the button? Please help me out!
You can do something like this :
import React from 'react';
import axios from 'axios';
import Blogpost from './Blogpost.js';
class BlogpostReader extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
blogposts: [],
count:5
};
}
componentDidMount() {
// Loading all blogposts in state
if(blogposts.length<5){
this.setState({
count:blogposts.length
})
}
}
renderBlogpost(i) {
// Render one blogpost
}
renderBlogposts(){
const blogposts=[];
const count=this.state.count;
for (let i = 1; i < count; i++) {
blogposts.push(
<div key={this.state.blogposts[i].id} className="col-12 col-sm-12 col-md-12 col-lg-6 col-xl-6 whole-blogpost">
{this.renderBlogpost(this.state.blogposts[i])}
</div>)
}
return blogposts;
}
//This function has to be replaced by one that renders extra blogposts
addMore=()=>{
let newCount=this.state.count + 2;
if(this.state.count===this.state.blogposts.length) return;
if(this.state.count+1 === this.state.blogposts.length){
newCount=this.state.count+1
}
this.setState({
count:newCount
})
}
render() {
const {error, isLoaded} = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
}
return (
<div>
<div className="row">
{this.renderBlogposts()}
</div>
<div className="centered-button">
<button className="styled-button" onClick={this.addMore}>Meer laden</button>
</div>
</div>
);
}
}
}
Oh no honey. In React the recommended approach is to make things as declarative as possible. Which means that instead of imperatively pushing items onto an array and then render that array you can just render a slice of the array.
I.e. try something like this
class BlogpostReader extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
blogposts: [], // this will contain all posts
postsToShow: 2, // a simple number controls how many posts are shown
};
}
componentDidMount() {
// Loading all blogposts in state
}
increasePostsShown() {
this.setState(({ postsToShow }) => {
postsToShow: postsToShow + 1;
});
}
render() {
const { error, isLoaded, blogposts, postsToShow } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
}
const postsShown = blogposts.slice(0, postsToShow); // get only the ones you want to show
return (
<div>
<div className="row">
{postsShown.map(blog => (
<div>{blog}</div> {/* or display them however you like */}
))}
</div>
<div className="centered-button">
<button className="styled-button" onClick={this.increasePostsShown}>
Meer laden
</button>
</div>
</div>
);
}
}
Is your blogpost array containing already all the blog posts? My suggestion would be that everytime the user clicks on the button, you increment a value from the state.
this.state = {
error: null,
isLoaded: false,
blogposts: [],
nbPostToDisplay: 4
};
In your loop:
for (let i = 0 /* start at 0, not 1 */; i < this.state.blogposts.length && i < nbPostToDisplay; i++) {
Some function to increment:
function incrementNbPosts() {
this.setState(prevState => return ({nbPOstsToDisplay: prevState.nbPostsToDisplay + 2});
}
Use function above in your button callback. This will trigger a re-render of your component.
IMPORTANT: do not forget to bind your functions in the construrtor or (better) use ES6 notation.
I would better keep things simple, so that button would just set new state.posts with +1 post, thus triggering render(), that in turn will render added element.
addPost = () => {
...
this.setState({
posts: [...posts, { id: posts.length + 1 }]
});
};
renderPosts = () => {
...
};
render() {
return (
<div>
<button onClick={this.addPost}>Add</button>
{this.renderPosts()}
</div>
);
}
Made a quick sandbox illustrating provided code.
https://codesandbox.io/embed/vjlp468jk7
Here's all you need. I also cleaned up your code a little bit
class BlogpostReader extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
blogposts: [],
limit: 4
};
this.showMore = this.showMore.bind(this);
this.renderBlogpost = this.renderBlogpost.bind(this);
}
componentDidMount() {
// Loading all blogposts in state
}
renderBlogpost(i) {
// Render one blogpost
}
//This function has to be replaced by one that renders extra blogposts
showMore() {
this.setState(state => ({ limit: state.limit + 2 }));
}
render() {
const { error, isLoaded, blogpost, limit } = this.state;
if (error) {
return <div>Error: {error.message}</div>);
}
if (!isLoaded) {
return <div>Loading...</div>;
}
return (
<div>
<div className="row">
{
blogposts.map((post, index) => {
if (index + 1 !== limit) {
return (
<div key={post.id} className="col-12 col-sm-12 col-md-12 col-lg-6 col-xl-6 whole-blogpost">
{ this.renderBlogpost(post) }
</div>
);
}
})
}
</div>
<div className="centered-button">
<button className="styled-button" onClick={this.showMore}>Meer laden</button>
</div>
</div>
);
}
}
If you want to also make showMore to accept any number of posts, you can do this...
showMore(value = 2) {
this.setState(state => ({ limit: state.limit + value }));
}
Then now you can call it with any number of posts you want. If you don't specify any value, the limit will be incremented by 2.
UPDATE
Since you've mentioned that you have to start when index is 1, then you can update your blogposts.map in the render like this
{
blogposts.map((post, index) => {
if (index && index !== limit) {
// the condition above ensures that the posts at index 0, and also when index equals limit, are not returned
// the rest of the code goes here...
}
})
}
After doing that, you can set limit to 5 in the constructor if you want to show only 4 entries at first load.
The following was the working code:
class BlogpostReader extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
blogposts: [],
limit: 5,
start: 1
};
this.showMore = this.showMore.bind(this);
}
componentDidMount() {
// Loading all blogposts into state
}
renderBlogpost(i) {
// Render a single blogost
}
showMore(){
this.setState(state => ({
start: state.limit,
limit: state.limit + 2
}));
}
render() {
const {error, isLoaded, limit} = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
var startedAt = this.state.start
for (startedAt; startedAt < this.state.blogposts.length && startedAt < limit; startedAt++) {
this.state.renderedBlogposts.push(
<div key={this.state.blogposts[startedAt].id} className="col-12 col-sm-12 col-md-12 col-lg-6 col-xl-6 whole-blogpost">
{this.renderBlogpost(this.state.blogposts[startedAt])}
</div>
)
}
return (
<div>
<div className="row">
{this.state.renderedBlogposts}
</div>
<div className="centered-button">
<button className="styled-button" onClick={this.showMore}>Meer laden</button>
</div>
</div>
);
}
}
}