How to render multiple component in reactjs, what i'm doing wrong? - reactjs

First i have a function to fetch data from database then
if the data be changed, i will create list components.
but it didnt work, what i'm doing wrong?
console:
class TweetContainer extends React.Component{
constructor(props){
super(props);
this.state = {
tweetData:{},
tweetRender : [],
listTweet:[]
}
}
here is my function to fetch data from database
componentDidMount(){
fetch('http://localhost:5000/tweet')
.then(function(response) {
return response.json();
})
.then(result=>{
this.setState({
tweetData: result
}, ()=>console.log(this.state.tweetData));
});
}
my function to make list component
componentDidUpdate(){
this.state.tweetRender = this.state.tweetData.data.slice(1,6);
console.log(this.state.tweetRender);
this.state.listTweet = this.state.tweetRender.map((tweet)=><Tweet
linkAvatar={'/image/jennyshen.jpg'}
name={"Vuongxuan"}
userName={'#vuggg'}
tweetText={tweet.content} />);
console.log(this.state.listTweet);
}
render(){
return(
<div id="main">
<h2>Tweet</h2>
<div id="stream">
{this.state.listTweet}
</div>
</div>
);
}
}
i dont know what i'm doing wrong.

Accordingly to React docs, componentDidMount lifecycle most common use is for:
Updating the DOM in response to prop or state changes.
And you want to get and render the tweets, right? Not necessarily listen to updates.
For now a solution is remove your componentDidUpdate() method and change your `render´ method to:
render(){
var tweetRender = this.state.tweetData.data.slice(1,6);
return(
<div id="main">
<h2>Tweet</h2>
<div id="stream">
{listTweet.map((tweet, idx) =>
<Tweet
key={idx}
linkAvatar={'/image/jennyshen.jpg'}
name={"Vuongxuan"}
userName={'#vuggg'}
tweetText={tweet.content} />
)}
</div>
</div>
);
}

It's generally not a good idea to put React elements (JSX) inside your component state. You could instead just store the data in state, and derive the JSX from that data in the render method.
Example
class TweetContainer extends React.Component {
state = {
tweetData: [],
tweetRender: [],
listTweet: []
};
componentDidMount() {
setTimeout(() => {
this.setState({
tweetData: [
{
id: 1,
name: "foo",
username: "#foo"
},
{
id: 2,
name: "bar",
username: "#bar"
}
]
});
}, 1000);
}
render() {
return (
<div id="main">
<h2>Tweet</h2>
<div id="stream">
{this.state.tweetData.map(obj => (
<div key={obj.id}>
{obj.username} - {obj.name}
</div>
))}
</div>
</div>
);
}
}
ReactDOM.render(<TweetContainer />, 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>

Related

React state doesn't seem to be fetching api and setting state before render

I'm trying to fetch an api of quotes and populate the react component with the first one. Later I'll use the button to pick a random one. I'm just now learning react, my react tutorial in freecodecamp didn't show anything about fetch so I found the code to pull these quotes online. If I add another callback after the this.setState I can console.log and see all the arrays but even with the if statement in the render it doesn't seem to be there in the state to render. What am I missing about setting the state or getting the component to render after the state has set to the array. I have already looked at this stackoverflow question.
class Quotes extends React.Component{
constructor(props){
super(props)
this.state = {
quotes: []
}
}
componentDidMount() {
fetch("https://type.fit/api/quotes")
.then((response) => response.json())
.then(quotesList => {
this.setState({ quotes: quotesList });
});
}
render(){
if (!this.state.quotes) {
return <div />
}
return(
<div>
<p id="text">{this.state.quotes[0].text}</p>
<p id="author">{this.state.quotes[0].author}</p>
<div id="buttons">
<button id="new-quote">New Quote</button>
<a id="tweet-quote" href="#"><i className="fa-brands fa-twitter"></i></a>
</div>
</div>
);
}
}
class QuoteBox extends React.Component{
constructor(props){
super(props)
}
render(){
return(
<div id="quote-box">
<Quotes />
</div>
);
}
}
ReactDOM.render(<QuoteBox />, document.getElementById('page-wrapper'))
#page-wrapper{
#quote-box{
display:flex;
height:100vh;
justify-content:center;
align-items:center;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="page-wrapper">
</div>
An empty array [] is not a falsy value, consequently your if does not get triggered and an out of bounds array access is done. Check for array length in your if instead and it will work.
See this thread on StackOverflow which covers truthy and falsy values.
Here your code with the condition within the if changed to
this.state.quotes.length === 0.
class Quotes extends React.Component {
constructor(props) {
super(props);
this.state = {
quotes: [],
};
}
componentDidMount() {
fetch("https://type.fit/api/quotes")
.then((response) => response.json())
.then((quotesList) => {
this.setState({ quotes: quotesList });
});
}
render() {
// check for array length here
if (this.state.quotes.length === 0) {
return <div>Fetching data...</div>;
}
return (
<div>
<p id="text">{this.state.quotes[0].text}</p>
<p id="author">{this.state.quotes[0].author}</p>
<div id="buttons">
<button id="new-quote">New Quote</button>
<a id="tweet-quote" href="#">
<i className="fa-brands fa-twitter"></i>
</a>
</div>
</div>
);
}
}
class QuoteBox extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div id="quote-box">
<Quotes />
</div>
);
}
}
ReactDOM.render(<QuoteBox />, document.getElementById("page-wrapper"));
#page-wrapper{
#quote-box{
display:flex;
height:100vh;
justify-content:center;
align-items:center;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="page-wrapper">
</div>

sort items in state alphabetically

I have a class based React component that is using items in state and rendering result. Here is short snippet how I do this:
class Menu extends Component {
constructor(props) {
super(props);
this.state = {
items: props.items.edges,
someItems: props.items.edges,
}
}
render() {
if (this.state.items.length > 0) {
return (
<div className="container">
<div className="row">
{this.state.someItems.map(({ node }) => {
return (
<div key={node.id}>
<div>
render some data
</div>
</div>
)
})}
</div>
</div>
);
}
}
}
The data is received as objects inside an array, like this:
My question is would it be possible to sort these items alphabetically before being rendered? What would be the best approach for this?
The best approach is to sort the items before you set them to the state. You can use the built in Array.prototype.sort method in order to sort the items. You can use the String.prototype.localeCompare in order to compare strings alphabetically.
I don't know the expected structure of your data so here is a general solution.
class App extends React.Component {
constructor(props) {
super(props);
// Make a copy so as not to modify the original array directly
const sortedCopy = [...props.items];
sortedCopy.sort((a, b) => a.name.localeCompare(b.name));
this.state = {
items: sortedCopy,
};
}
render() {
return (
<div>
{this.state.items.map((item) => (
<p key={item.id}>
<div>Item - {item.name}</div>
</p>
))}
</div>
);
}
}
// Example items prop is out of order
const items = [
{ id: 0, name: "C" },
{ id: 1, name: "B" },
{ id: 2, name: "A" },
{ id: 3, name: "D" },
];
ReactDOM.render(<App items={items} />, 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>

Learning react with a click game, onClick won't fire. I think I'm just passing a prop *called* onClick

The Bit component is supposed to be my clickable, which should be incrementing the state due to my mine function in the Mine component.
function Bit(props) {
return (
<img src={logo} className="App-logo" alt="logo" onClick={props.onClick} />
)
}
class Mine extends React.Component {
constructor(props) {
super(props);
this.state = {
bitCoins: 0,
clickBonus: 1,
cps: 1,
}
}
mine() {
alert('here')
this.setState({
bitCoins: this.state.bitCoins + 1
})
console.log(this.state.bitCoins);
}
render() {
let status;
status = this.state.bitCoins
return (
<div>
<Bit onClick={() => this.mine()} />
</div>
<div className="text-primary">{status}</div>
)
}
}
What is returned from render in React cannot have sibling elements at the top level. So just wrapping what you're returning with <React.Fragment> (or a div or whatever else you choose) fixed it.
Also note that setState is asynchronous, so when you console.log immediately after calling it, you may not get the most up to date values.
class Mine extends React.Component {
constructor(props) {
super(props);
this.state = {
bitCoins: 0,
clickBonus: 1,
cps: 1,
}
}
mine() {
alert('here')
this.setState({
bitCoins: this.state.bitCoins + 1
})
console.log(this.state.bitCoins);
}
render() {
let status;
status = this.state.bitCoins
return (
<React.Fragment>
<div>
<button onClick={() => this.mine()}>Mine</button>
</div>
<div className="text-primary">{status}</div>
</React.Fragment>
)
}
}
ReactDOM.render(
<Mine />,
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>

Render a string which contains components

I am newbie at reactJs and i am trying to build an app in which i get some results after searching .
My problem is that i have a component called ResultEntity and I am trying create a dynamically page without defined number of ResultEntity components.
I tried something like this
for(var i=0 ; i<result.length ; i++)
{
results += "<div> <ResultEntity/> </div>";
};
console.log(results);
this.setState({result: results});
And i tried to return it like ,
return (
<div>
<div dangerouslySetInnerHTML={{ __html: this.state.result }} />
</div>
);
and
return (
<div>
<div dangerouslySetInnerHTML={{ __html: this.state.result }} />
</div>
);
but both didnt work . Any idea will be appreciated . Thank you in advance
So you want to render a list of components dynamically. Here's how you can do it using .map function:
// This also can be a functional component, instead of a class
class ResultEntity extends React.Component {
render() {
const { item } = this.props
return <div>{ `${item.id} - ${item.name}` }</div>
}
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
items: [
{ id: 1, name: 'Bulgaria' },
{ id: 2, name: 'Germany' },
]
}
}
renderItems() {
const { items } = this.state
// Great explanations, why this work and it's rendered correctly:
// https://medium.com/byte-sized-react/component-arrays-in-react-a46e775fae7b
return items.map(item => <ResultEntity key={item.id} item={item} />)
}
render() {
// From React >= 16 it's possible to skip the wrapper `div`:
// https://stackoverflow.com/a/32157488/4312466
return <div>{ this.renderItems() }</div>
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>

Why is my component not getting re-rendered upon using setState and changing the value of state?

Why is it that the component Eachcartitem is not getting re rendered although I change the state. I have this fucntion which gets called from inside the Eachcartitem component:-
cartremover(a){
var cart1=cart;
var firstpart=cart.slice(0,a);
var secondpart=cart.slice(a+1,cart.length);
var final = firstpart.concat(secondpart);
this.setState({
cartstate:final,
abc:true
})
}
The Eachcartitem is used as follows in parent component:-
<div style={{float:'left',width:'65%'}}>
{
this.state.cartstate.map((cat) => (<Eachcartitem data={cat} index={this.state.cartstate.indexOf(cat)} cartremover={i=>this.cartremover()}/>))
}
<br></br><br></br>
</div>
And the Eachcartitem is as follows:-
class Eachcartitem extends React.Component{
constructor(props){
super(props);
this.state={
data:this.props.data
};
}
clicker(){
this.props.cartremover(this.props.index);
}
render(){
return(
<div className='cartdiv'>
<div style={{width:'100%',display:'inline'}}>
<h3 style={{width:'70%',float:'left',paddingLeft:'10px'}}>{this.state.data.productName}</h3>
<div style={{float:'right'}}>Rs.{this.state.data.productPrice}</div>
<div style={{width:'30%',float:'left',paddingLeft:'10px'}}>Store:{this.state.data.shopName}</div>
<div style={{width:'30%',float:'left',paddingLeft:'10px'}}>Quantity:{this.state.data.productQuantity}</div>
<br></br><br></br><br></br><br></br><br></br>
<div style={{width:'auto',float:'left',paddingLeft:'10px'}}>Variant:{this.state.data.variant.quantity}</div>
<br></br><br></br><br></br><br></br>
<div style={{width:'auto',float:'right',marginRight:'7px'}} onClick={()=>this.clicker()}>❌</div>
</div>
</div>
);
}
}
export default Eachcartitem
But for some reason the cartitem divs are not getting changed why is it so?
Its because you are not passing the index of the item to the function, here:
cartremover={i => this.cartremover()}
Write it like this:
cartremover={i => this.cartremover(i)}
You don't need to pass the index to child component, use this code:
this.state.cartstate.map((cat, i) => (
<Eachcartitem data={cat} cartremover={e => this.cartremover(i)} />
))
Now from Eachcartitem simply call that method: this.props.cartremover().
Better to use splice to remove the element at particular index, write the method like this:
cartremover(a){
let cart = [...this.state.cartstate];
cart.splice(a, 1);
this.setState({
cartstate: cart,
abc: true
})
}
Check working snippet:
const Temp = (props) => <div>
{props.name}
<button onClick={props.cartremover}>Delete</button>
</div>
class App extends React.Component {
constructor() {
super()
this.state = {cart: ['a', 'b', 'c', 'd', 'e']}
}
cartremover(a){
let cart = [...this.state.cart];
cart.splice(a, 1);
this.setState({
cart,
})
}
render() {
return (
<div>
{this.state.cart.map((cart, i) => (
<Temp key={cart} name={cart} cartremover={e => this.cartremover(i)}/>
))}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app' />

Resources