why is my component getting rendered once but then failing on refresh - reactjs

i am working on small react assignment,
following is my component code. So my component is getting rendered once but then it just fails.i'll attach the screenshots too, can some one please explain what is happening?is there an error in the code or is it because of some rate limiting in API i am using?
import React from 'react'
const Menu = ({events}) => {
console.log(events);
return (
<div>
{events.map((event)=>{
return( <div key={event.category}>
<h3>{event.category}</h3>
</div>)
})}
</div>
)
}
export default Menu
code working image
error on same code pic
parent component code
import React,{useState,useEffect} from 'react';
import './App.css';
import Menu from './components/Menu';
function App() {
const [isLoading,setISLoading] = useState(true);
const[events,setEvents] = useState()
const getEvents = async()=>{
const response = await fetch('https://allevents.s3.amazonaws.com/tests/categories.json');
const eventsData =await response.json()
setISLoading(false);
setEvents(eventsData);
}
useEffect(()=>getEvents(),[]);
return (
isLoading?<h1>Loading...</h1>:<Menu events = {events}/>
);
}
export default App;

May be the parent component of Menu which is supplying events is not using any loading state. So when the component is mounted and starts making ajax calls, events is undefined. You need to put a condition over there like this:
import React from 'react'
const Menu = ({events}) => {
console.log(events);
return events ? (
<div>
{events.map((event)=>{
return( <div key={event.category}>
<h3>{event.category}</h3>
</div>)
})}
</div>
) : null
}
export default Menu

Related

Add functional component dynamical in react with out using class component

I am trying to make a list of skills that are supposed to build in UI and I didn't have any problem in this step.
But when I add a new item to the list by writing it in input and clicking on a button to add it, the console sends a massage to tell: skills is not iterable.
this is a code in the App.js file:
import './App.css';
import Skills from './Components/Details'
import React,{useState} from 'react';
function App() {
const [numApp,reNumApp] = useState(0)
const [skill,reSkill] = useState('')
const [skills,reSkills] = useState(['HTML'])
const addSkill = (value)=>{
let newList = skills.push(value);
reSkills(newList);
console.log(skills);
}
console.log(skills)
return(
<>
<h1>Num Of applecation:{numApp}</h1>
<button onClick={()=>reNumApp(numApp + 1)}>Other Application</button>
<hr/>
<input type='text' value={skill} onChange={(e)=>reSkill(e.target.value)}/>
<button onClick={()=>{addSkill(skill)}}>Add</button>
<Skills skills = {skills}/>
</>
)
}
export default App;
And this is the code in the Details file:
import React,{useState,Fragment} from 'react';
function Skill({key,value})
{
const [count,reCount] = useState(0)
return(
<>
<span><p key={key}>{value} : {count}</p></span>
<button onClick={()=>reCount(count + 1)}>+</button>
</>
)
}
function Skills({skills}) {
return(
<Fragment>
{
[...skills].map((v,i)=>{return <Skill key={i} value={v}/>})
}
</Fragment>
)
}
export default Skills
I need to know how can I solve this problem without using the class component.
Try not to push on the current state, so change this:
let newList = skills.push(value);
reSkills(newList);
to this:
reSkills([...skills, value]);

React render instagram feeds from data.json

I"m trying to render username comments with likes and a like. please see image on demo.
All of this comes form data.json
I can't find a way to display the json properly on the tags. What am I missing here?
Sorry I'm trying my best here with react as I'm quite a beginner.
demo
my index.js
import React from "react";
import styles from "./styles";
import { getCaptionFromEdges } from "./helpers";
const Posts = (props) => {
const { data } = props;
return (
<img src={data.owner.profile_pic_url} /> // this tag works
<p>{data.owner.node.username}</p> // this tag doesn't work
<hr>
//here I should display all comments with its like.
<p>{data.node.text}</p>// this doesn't work
);
};
export default Posts;
You need to wrap your elements with another element (or a Fragment). Try the following:
import React from "react";
import styles from "./styles";
import { getCaptionFromEdges } from "./helpers";
const Posts = (props) => {
const { data } = props;
return (
<>
<img src={data.owner.profile_pic_url} />
<p>{data.owner.node.username}</p>
<hr />
<p>{data.node.text}</p>
</>
);
};
export default Posts;

How to prevent component from being re-rendered unnecessarily

I'll start with the code. I have a stateless functional component that resembles this
export const Edit Topic = (_title, _text) {
const [title, setTitle] = useState(_title)
const [text, setText] = useState(_text)
return (
<>
<InputText props={{ fieldName:"Title:", value:title, setValue:setTitle, placeHolder:"Topic Title"}}/>
<InputTextArea props={{ fieldName:"Markdown Text:", text, setText }}/>
<PreviewBox text={text}/>
</>
)
}
I have PreviewBox when it's on, page rendering takes a bit longer because text can be quite long. PreviewBox needs to re-render each time I change text in InputTextArea and that's fine.
The problem I'm having is when I change the value of title it's also updating <PreviewBox/> which is undesired.
How can I make sure that <PreviewBox/> only updates when text changes and not when title changes?
The reason why I believe the re-rendering is occuring is because if I toggle off PreviewBox, there's no lag in when updating title but when PreviewBox is visible the updating the title lags.
import style from "../styles/CreateTopic.module.css"
import { Component } from "react"
import Markdown from "./Markdown";
export class PreviewBox extends Component {
constructor(props) {
super(props)
this.state = {
isShow: true
}
}
toggleShow = () => {
console.log("begin isShow", this.state)
this.setState(state => ({ isShow: !state.isShow}))
}
render() {
return (
<>
<div className={style.wrptoggle}>
<button className={style.btn} onClick={this.toggleShow}>Preview</button>
</div>
{this.state.isShow ?
<div className={style.wrppreviewbox}>
<div className={style.previewbox}>
<Markdown text={this.props.text}/>
</div>
</div>
: null}
</>
)
}
}
Since the above also contains <Markdown/> here's that component:
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
import ReactMarkdown from "react-markdown";
import "katex/dist/katex.min.css";
const Markdown = ({text}) => {
return (
<div>
<ReactMarkdown
remarkPlugins={[remarkMath]}
rehypePlugins={[rehypeKatex]}
children={text}
/>
</div>
);
}
export default Markdown;
I don't see any complexity in PreviewBox that would cause any rendering delay so I might assume it's the Markdown component that may take some time "working" when it's rerendered since you say "toggle off PreviewBox, there's no lag in when updating title".
Solution
You can use the memo Higher Order Component to decorate the Markdown component and provide a custom areEqual props compare function.
import { memo } from 'react';
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
import ReactMarkdown from "react-markdown";
import "katex/dist/katex.min.css";
const Markdown = ({ text }) => {
return (
<div>
<ReactMarkdown
remarkPlugins={[remarkMath]}
rehypePlugins={[rehypeKatex]}
children={text}
/>
</div>
);
};
export default memo(Markdown);
By default it will only shallowly compare complex objects in the props
object. If you want control over the comparison, you can also provide
a custom comparison function as the second argument.
const areEqual = (prevProps, nextProps) => {
return prevProps.text === nextProps.text;
};
export default memo(Markdown, areEqual);

Learning React: Trouble understanding why my component won't render/ API/KEYS

I am fairly new to learning React and need some help.
I want to create a meal finder app using the MealDb API, my problem is it won't render.
Here is my code, in my app component:
import React, { useState } from 'react';
import './App.css';
import Search from './components/Search';
import Meals from './components/Meals';
import axios from 'axios';
function App() {
const [meals, setMeals] = useState({});
const searchMeals = async meals => {
const res = await axios.get(
`https://www.themealdb.com/api/json/v1/1/search.php?s=${meals}`
);
console.log(res.data);
setMeals({ meals: res.data });
};
return (
<div className='App'>
<Search searchMeals={searchMeals}></Search>
<Meals searchMeals={searchMeals} meals={meals}></Meals>
</div>
);
}
export default App;
I created a component Meals with this inside:
import React from 'react'
import MealsItem from './MealsItem'
const Meals = ({ meals}) => {
return (
<div >
{Object.keys(meals).map(meal=>(
<MealsItem key={meal.idMeal} meal={meal}/>
))}
</div>
);
};
export default Meals
and then for the rendering part I created a MealsItems component:
import React, { Fragment } from 'react';
const MealsItem = ({ meal :{strMealThumb, strMeal,}}) => {
return (
<Fragment>
<div id='result-heading'>
<h2>Search result for: {strMeal}</h2>
</div>
<div className='meal'>
<img src={strMealThumb} alt={strMeal} />
<div className='meal-info'>
<h3>{strMeal}</h3>
</div>
</div>
</Fragment>
);
};
export default MealsItem;
When I search something: I see a unique item with nothing in it. No title, no image. In the console when I inspect the app I read {meals: Array(8)} and "Warning: Each child in a list should have a unique "key" prop." I thought did write a unique key: key={meal.idMeal}.
EDIT: I did try to write
{meals.map(meal=>(
))}
to no avail, unfortunately. It gives me a type error meals.map is not a function.
This is the structure of the API:
{
"meals": [
{
"idMeal": "52772",
"strMeal": "Teriyaki Chicken Casserole",
"strDrinkAlternate": null,
"strCategory": "Chicken"
}
]
}
Edit # 2: This is what I get when I console.log(meals)
console.log(meals)
Thank you to anyone willing to help! Have a good day!
Reason is that you are using
Object.keys(meals).map.
Instead, do this
{meals.map(meal=>(
<MealsItem key={meal.idMeal} meal={meal}/>
))}
EDIT: Also set default value of meals state.
const [meals, setMeals] = useState([]); // dont use useState({})
Otherwise in the initial render before meals are fetched there will be error as we try to map over an object.

App crashes after refresh

I have got a problem with my app. Everything is fine untill I press the refresh button. I assume it is happening because of some stuff is not ready to be rendered yet.
import React from 'react'
import { Meteor } from 'meteor/meteor'
import { createContainer } from 'meteor/react-meteor-data'
import { withRouter } from 'react-router'
import LeftNavbar from '../dashboard/LeftNavbar'
import UpperBar from '../dashboard/UpperBar'
import NewGreetingsForm from './NewGreetingsForm'
import ConfigureButtons from './ConfigureButtons'
import Fanpages from '../../../api/Fanpages.js'
import './Greetings.scss'
export class Greetings extends React.Component {
constructor (props) {
super(props)
this.fanpage = this.props.user.profile.fanpages
this.state = {
newGreetingsText: '',
newGreetingsCharCount: 0
}
}
componentDidMount () {
}
render () {
const currentFanpage = Fanpages.findOne({fanpageName: this.fanpage})
const currentGreeting = currentFanpage.fanpageInfo.fanpageInfo.config.greeting[0].text
return (
<div className='container page'>
<UpperBar title={'Konfiguracja fanpage / Zdefiniuj greetings'} />
<LeftNavbar />
<div className='main-content'>
<h4 id='main-title'>{this.fanpage}</h4>
<div className='container'>
<div className='row'>
<ConfigureButtons />
<div>
<h5 id='configure-content-right'>Zmień obecną informację</h5>
<NewGreetingsForm fanpageName={this.fanpage} placeholder={currentGreeting} />
</div>
</div>
</div>
</div>
</div>
)
}
}
export default withRouter(createContainer(() => ({
user: Meteor.user()
}), Greetings))
Any idea where I should move those variables from render method? So it works as it should after page refresh? Thanks a lot for any participation.
Assuming there's subscription management going on above this component you can just defend against the data not being ready:
render () {
const currentFanpage = Fanpages.findOne({fanpageName: this.fanpage});
if (!currentFanpage) {
return (
<div />
);
} else {
const currentGreeting = currentFanpage.fanpageInfo.config.greeting[0].text
return (
// your html
)
}
}
Better yet, have the parent component render a spinner until the subscription is .ready()
Update
You asked about using createContainer with withRouter. I don't normally use withRouter and I haven't tested this but it should go something like:
const container = () => {
const sub = Meteor.subscribe('mysub');
const loading = sub.ready();
const user = Meteor.user();
return { loading, user };
}
export default withRouter(container, Greetings))
The important thing being that the container have a loading key that's tied to the state of the subscription.
Container tutorial

Resources