React Function to Generate random name from Array error - reactjs

I'm getting an error with my component "TypeError: Cannot read property 'length' of undefined" from my RandomName component in my react app . What am I doing wrong?
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
function RandomName(props) {
const [name, newName] = useState(0);
const { names } = props;
return (
<div>
<h2>{name}</h2>
<button onClick={() => newName(names[Math.floor(Math.random()*names.length)])}>
New Name
</button>
</div>
);
}
ReactDOM.render(
<RandomName names={['Paul', 'David', 'Kevin']} />,
document.getElementById('root')
);
export default RandomName

You can generate an array first and then you use faker to put fake names in it.
const a = new Array(50).fill(null)
.map(e =>
e = faker.fake("{{name.lastName}}, {{name.firstName}}"))

Your code above seems to be correct but instead of 0 you can pass ''. But from Github repo I noticed you are not passing names in App.js.
<Switch>
<Route path="/">
<>
<RandomName names={['Paul', 'David', 'Kevin']} />
</>
</Route>
</Switch>
Updated Code

You have 3 ways
use typescript to check props data type
function RandomName({names=[]}:{names:Array<string>}){...}
use below code for check propTypes
import PropTypes from 'prop-types';
.
.
.
RandomName .propTypes = {
names: PropTypes.arrayOf(PropTypes.string)
}
and use below code to define function
function RandomName({names=[],...restprops}){...}

I was able to get the component working like so - thank you all for your help:
import React, { useState } from 'react';
import './index.css'
const names=['Mark', 'Steve', 'Tom']
function RandomName() {
const [name, newName] = useState('Bob');
return (
<div id="RandomName-container">
<h2>{name}</h2>
<button onClick={() => newName(names[Math.floor(Math.random()*names.length)])}>
New Name
</button>
</div>
);
}
export default RandomName

Related

How to use react-router-dom with Context API V6?

I am changing the value in PC component but it is not reflected in the BR1 component. If I don't use react-router-dom, everything works fine, but I need the routes.
App.js code
import React, { createContext, useState } from 'react';
import './App.css';
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import BR1 from './Components/BR1';
import PC from './Components/P_c1'
import BR from './Components/BR';
export const BRcontext = createContext();
function App() {
const [value, setValue] = useState(false)
return (
<div>
<BRcontext.Provider value={{value, setValue}}>
<Router>
<Routes>
<Route path='/PC' element={<PC/>} />
<Route path='/BR1' element={<BR1/>} />
<Route path='/BR' element={<BR/>} />
</Routes>
</Router>
</BRcontext.Provider>
</div>
);
}
export default App;
PC code
import React, { useContext } from 'react'
import './Profile.css';
import { BRcontext } from '../App';
export default function Profile() {
const {value, setValue} = useContext(BRcontext);
return (
<div>
<div className='container mt-5'>
<div className='row'>
<div>
<h3 className='mt-5'>Send Request</h3>
<button className='btn btn-success mt-3 ps-3 pe-3' onClick={()=>{setValue(true)}}>Request</button>
</div>
</div>
</div>
</div>
)
}
BR1 code
import React, { useContext } from 'react'
import BR from './BR'
import { BRcontext } from '../App'
import { Link } from 'react-router-dom';
export default function BR1() {
const {value} = useContext(BRcontext);
// let navigate = useNavigate();
return (
<div>
{console.log(value)} //this remains false
{value ? <Link to="/BR"/>: console.log('hello there!')}
</div>
)
}
In BR1 code, I want the value to become true when a button in the PC component is clicked
Link - https://codesandbox.io/s/great-star-bzhuvw?file=/src/App.js
It seems there's no way to navigate from /PC to /BR1 unless changing the browser URL directly, and by doing this, you lose the current context value because it's in memory. If you intend to keep this behaviour, you should consider persisting the context value every time you change it and initialize it with the previously persisted one.
An example using the browser's local storage:
// Helper function to read the storaged value if it exists
function getPersistedValue() {
const serializedValue = localStorage.getItem('value')
try {
if (!serializedValue) {
throw new Error('No previously persisted value found')
}
return JSON.parse(serializedValue)
} catch {
return false
}
}
// Using the helper function to initialize the state
const [value, setValue] = useState(getPersistedValue())
// Synchronizing the persisted value on local storage with the in-memory one
useEffect(() => {
localStorage.setItem('value', JSON.stringify(value))
}, [value])
If you want, I forked your Code Sandbox and applied these changes: https://codesandbox.io/s/router-context-forked-uqhzye.

Map through two arrays of components and strings and render in one component

I have two arrays that I want to map through:
const social = ["Snapchat", "TikTok", "Dribbble", "Discord", "Facebook"];
const socialIcons = [<SnapchatIcon />, <DribbbleIcon />];
The socialIcons array are all components
How can I send both values as props into my DummyRectangle component? Here is my current code:
{social.map((s, index) => (
<div className="dummy_buttonsWrapper">
<DummRectangle social={s} socialIcons={i} />
</div>
))}
And here is DummyRectangle component:
function DummRectangle({ social, socialIcons }) {
// console.log("---->", socialIcons);
return (
<div>
<p>{social}</p>
{<socialIcon/>} // render social icon component
</div>
);
}
To do so, you don't need to wrap tags around your socialIcon in your DummRectangle. Also, it doesn't seem that you are passing the socialIcon component at all. If I were you, I would do something like this:
The following two are the components as an example that you would like to render (in your case - socialIcons)
// Comp1.js
import React from "react";
const Comp1 = () => <div>actual Comp1</div>;
export default Comp1;
// Comp2.js
import React from "react";
const Comp2 = () => <div>actual Comp2</div>;
export default Comp2;
Now, in your main Parent component, you would simply get the current component of the componentName (in your case - social) by accessing your component's array with an index. Then, you would pass this currentComponent as props to your Child component where you want to render it.
// App.js
import React from "react";
import Comp1 from "./Comp1";
import Comp2 from "./Comp2";
import DummyComponent from "./DummyComponent";
export default function App() {
const componentNames = ["Comp1", "Comp2"];
const components = [<Comp1 />, <Comp2 />];
return (
<div className="App">
{componentNames.map((name, index) => {
const currentComponent = components[index];
return (
<div>
<DummyComponent componentName={name} component={currentComponent} />
</div>
);
})}
</div>
);
}
In your Child component, you can simply render it by enclosing it into the brackets - no need to add tags. React will do all the rendering for you. In your case it would be { socialIcon }
// DummyComponent.js
import React from "react";
const DummyComponent = ({ componentName, component }) => {
return (
<div>
<p>{componentName}</p>
{component}
</div>
);
};
export default DummyComponent;
Link to Codesandbox with the above code for reference: click here

How do i get data from react hook.?

Im trying to get data , from an API and then consume it from another hook component.
But I have this error
This is my App.js
import React, {useEffect, useState} from 'react';
import {BrowserRouter as Router, Link} from 'react-router-dom';
import MainRouter from "./core/MainRouter";
import {MovieProvider} from "./context/MovieContext";
import axios from 'axios';
function App() {
const [tagCat,setTagCat] = useState([]);
const getMovieInfo = async ()=>{
const res = await axios.get('/get/tag_category');
//for testing
setTagCat(res.data.data)
};
useEffect(()=>{
getMovieInfo();
},[]);
return (
<MovieProvider value={tagCat}>
<Router>
<MainRouter/>
</Router>
</MovieProvider>
);
}
export default App;
MovieContext.js
import React, {useContext} from "react";
const MovieContext = React.createContext({});
const MovieProvider = MovieContext.Provider;
const MovieConsumer = MovieContext.Consumer;
export default MovieContext;
export {MovieProvider,MovieConsumer};
consume from that PageNav component
PageNav.js
import React, {useContext, useEffect, useState} from 'react';
import {Link} from 'react-router-dom';
import MovieContext,{MovieConsumer} from "../../context/MovieContext";
const PageNav = ()=>{
const tagCat = useContext(MovieContext);
const [category,setCategory] = useState([]);
useEffect(()=>{
setCategory(tagCat.main_category);
})
return (
<React.Fragment>
<div>
<header className="content__title">
<h1>Welcome! (Mingalarpar) <small>
Feel Free to use any data , btw we need more suggest from you.
</small></h1>
</header>
<div className="toolbar">
<nav className="toolbar__nav">
**{console.log(category[0])}**
<a className="active" href="#">Following</a>
Groups
</nav>
</div>
</div>
</React.Fragment>
)
};
export default PageNav;
I see the data in devtool,
but i cant map the data
i.e.
category.map()//error
Thank you
The value in ContextProvider is fetched asynchronously and hence won't be available on initial render of the component. If you try to set a specific field from it in state of PageNav component, it will throw you can error
You need to check for its existence before using, also you need not store the value obtained from context in state since you can directly derive it
const PageNav = ()=>{
const tagCat = useContext(MovieContext);
const [category,setCategory] = useState([]);
return (
<React.Fragment>
<div>
{/* othercode */}
<div className="toolbar">
<nav className="toolbar__nav">
{tatCat.main_category && tatCat.main_category.map(() => {})}
<a className="active" href="#">Following</a>
Groups
</nav>
</div>
</div>
</React.Fragment>
)
};
export default PageNav;
You are not passing object in Context provider in App.js. value={tagcat} will be replaced by value={{tagcat}} Try this
<MovieProvider value={{tagCat}}>
<Router>
<MainRouter/>
</Router>
</MovieProvider>

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.

Is there a way in React Javascript to pass props and use it in external import?

I want to pass props from one component to another, and use it in the second one for an import above the component declaration
This is for using the same component, with no need to create it 4 times, every time with another SVG.
I'm using React, Javascript, Webpack, babel.
I'm also using svgr/webpack to create a component from an SVG picture, and it's crucial for me to use SVG not < img >.
import React from 'react';
import RightNavItem from './right_nav_item';
const RightNav = ({navitems}) => {
const rightNavItems = navitems.map( (item) => {
return <RightNavItem name={ item }/>
});
return(
<div className="rightnav">
{rightNavItems}
</div>
);
};
.
export default RightNav;
import React from 'react';
const RightNavItem = ({ name }) => {
const svgpath = `../../../../resources/img/navbar/${name}.svg`;
return(
<div>
<img src={ svgpath } style={{height: '25px'}}/>
<span>{ name }</span>
</div>
);
};
export default RightNavItem;
And I want to achieve being able to do this:
import React from 'react';
import SvgPicture from '../../../../resources/img/navbar/{name}.svg';
const RightNavItem = ({ name }) => {
return(
<div>
<SvgPicture />
<span>{ name }</span>
</div>
);
};
export default RightNavItem;
.
Ok so I went back and implemented the whole thing on my local app to get exactly what you need. I am editing my original answer. Hope this solves your issue.
The parent:
import React from 'react';
import { ReactComponent as svg } from 'assets/img/free_sample.svg';
import RightNavItem from './RightNavItem';
const LOGOS = [
{ name: 'home', svg },
{ name: 'home', svg },
];
const RightNav = () => (
<div>
{LOGOS.map(logo => (
<RightNavItem name={logo.name}>
<logo.svg />
</RightNavItem>
))}
</div>
);
export default RightNav;
The child:
import React from 'react';
const RightNavItem = ({ name, children }) => (
<div>
{children}
<span>{name}</span>
</div>
);
export default RightNavItem;
You don't need to import the svg as I did, if you are able to use svg as a component in your webpack config then continue to do what you were doing before.
I managed to do it in a kind of ugly way, but it works.
The problem is if I have more than 4 items, then using it without the map() function can be really annoying.
I used {props.children}, and instead of using map(), I added the 4 times, each with different 'SVG' component child and different props 'name', that way the component only gets initialized at the RightNavItem level.
IF SOMEONE KNOWS how can I use this with the map() function, It'll help a lot!
Thanks to everyone who helped!
For example:
const RightNav = (props) => {
return(
<div className = "rightnav">
<RightNavItem name = {home}>
<HomeSVG />
</RightNavItem>
<RightNavItem name = {profile}>
<ProfileSVG />
</RightNavItem>
.
.
.
</div>
);
};
And in the RightNavItem:
const RightNavItem = (props) => {
return(
<div>
{props.children}
<span>{ props.name }</span>
</div>
);
};

Resources