I'm trying to render out a list of titles by calling map on state from inside the <MyContext.Consumer> component but the code is not returning the list items. However, if I do a console.log(dataImTryingToMap); it shows exactly what I'm trying to render. The <ul> is rendered but no <li>'s are. There are no errors thrown by create-react-app. What am I missing??? Here is the consumer component:
<MyContext.Consumer>
{context => (
<ul>
{Object.keys(context.state.subjects).map(subject => {
<li>{context.state.subjects[subject].title}</li>;
console.log(context.state.subjects[subject].title);
})}
</ul>
)}
</MyContext.Consumer>
The console log returns exactly the data I'm looking for, but nothing shows on the screen.
And here is the state from the <MyContext.MyProvider> component:
state = {
subjects: {
subject0: {
title: "Math",
description: "",
cards: {
card1: {
note: "",
answer: ""
}
}
},
subject1: {
title: "history",
description: "",
cards: {
card1: {
note: "",
answer: ""
}
}
}
}
};
Thanks for any help!
You are not returning anything from the Array.map() callback:
{Object.keys(context.state.subjects).map(subject => {
<li>{context.state.subjects[subject].title}</li>; <-- this is not returned
console.log(context.state.subjects[subject].title);
})}
const contextValue = { state: {"subjects":{"subject0":{"title":"Math","description":"","cards":{"card1":{"note":"","answer":""}}},"subject1":{"title":"history","description":"","cards":{"card1":{"note":"","answer":""}}}}}};
const MyContext = React.createContext();
const Example = () => (
<MyContext.Consumer>
{context => (
<ul>
{Object.keys(context.state.subjects).map(subject => {
console.log(context.state.subjects[subject].title);
return (
<li key={subject}>{context.state.subjects[subject].title}</li>
);
})}
</ul>
)}
</MyContext.Consumer>
);
const Demo = ({ value }) => (
<MyContext.Provider value={value}>
<Example />
</MyContext.Provider>
);
ReactDOM.render(
<Demo value={contextValue} />,
demo
);
<script crossorigin src="https://unpkg.com/react#16.3/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16.3/umd/react-dom.development.js"></script>
<div id="demo">
</demo>
You haven't returned anything from inside the map function and hence it doesn't render anything. Either return it explicitly like
<MyContext.Consumer>
{context => (
<ul>
{Object.keys(context.state.subjects).map(subject => {
return <li>{context.state.subjects[subject].title}</li>;
})}
</ul>
)}
</MyContext.Consumer>
or implicitly like
<MyContext.Consumer>
{context => (
<ul>
{Object.keys(context.state.subjects).map(subject => (
<li>{context.state.subjects[subject].title}</li>;
))}
</ul>
)}
</MyContext.Consumer>
Related
In my react app I am fetching data from API, the response object is as below:-
posts {
total: 3,
data:[
{some data},
{some data},
{some data}
] }
The data is rendered using a map() function.
but whenever the page renders the data is displayed only once. After the first render when the page is re-rendered the data array is undefined (in console.log).
code for component:-
const function = () => {
const dispatch = useDispatch();
const { posts, isSuccess } = useSelector((state) => state.posts);
useEffect(() => {
dispatch(getPosts());
}, [dispatch]);
return (
<>
<div className="main-content">
{posts.data.map((post) => (
<Post
postId={post._id}
postSubject={post.subject}
/>
))}
</div>
</>
);
}
export default function;
You can try this before map function.
<React.Fragment>
{typeof object.data === typeof [] && (
<React.Fragment>
{object.data.map((obj, index) => {
return <React.Fragment>return some code .....</React.Fragment>;
})}
</React.Fragment>
)}
</React.Fragment>
I hope it's work
I have data in for of list of lists and I want to render it in the view.
The data:
{
"blocks_content": [
[
" Bakery",
" Company GbR",
],
[
"Bill no.3-7721 11:29:51 20.04.2021"
],
[
"1x Cappuccino 2,70€ 2,70€ A"
],
]
}
I tried like this but I am not seeing any error or warning. what I could be missing?
return (
<Container>
....
{
fileBlocks !== null &&
<Form.Field>
<Label>{fileBlocks["name"]}</Label>
{fileBlocks["blocks_content"].forEach((element, index) => {
<List key={index}>
{
element.forEach((line, i) => {
<List.Item key={i}>
{line}
</List.Item>
})
}
</List>
})}
</Form.Field>
}
...
</Container>
);
use map instead of forEach .
{fileBlocks["blocks_content"].map((element, index) => (
<List key={index}>
{
element.map((line, i) => (
<List.Item key={i}>
{line}
</List.Item>
))
}
</List>
))}
Rendering Lists In React
forEach only processes arrays in place but doesn't return anything. map returns a new array of combined array information and JSX which React can use in the render.
function Example({ data }) {
return (
<div>
{data.blocks_content.map(arr => {
return (
<ul>
{arr.map((line, i) => <li key={i}>{line}</li>)}
</ul>
)
})}
</div>
);
}
const data = {
blocks_content: [
['Bakery', 'Company GbR'],
['Bill no.3-7721 11:29:51 20.04.2021'],
['1x Cappuccino 2,70€ 2,70€ A'],
]
};
// Render it
ReactDOM.render(
<Example data={data} />,
document.getElementById("react")
);
<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="react"></div>
I make this mistake all the time! You need to use .map() instead of .forEach()
Before diving to the main problem, my use case is I am trying to handle the scroll to a desired section. I will have navigations on the left and list of form sections relative to those navigation on the right. The Navigation and Form Section are the child component. Here is how I have structured my code
Parent.js
const scrollToRef = ref => window.scrollTo(0, ref.current.offsetTop);
const Profile = () => {
const socialRef = React.useRef(null);
const smsRef = React.useRef(null);
const handleScroll = ref => {
console.log("scrollRef", ref);
scrollToRef(ref);
};
return (
<>
<Wrapper>
<Grid>
<Row>
<Col xs={12} md={3} sm={12}>
<Navigation
socialRef={socialRef}
smsRef={smsRef}
handleScroll={handleScroll}
/>
</Col>
<Col xs={12} md={9} sm={12}>
<Form
socialRef={socialRef}
smsRef={smsRef}
/>
</Col>
</Row>
</Grid>
</Wrapper>
</>
);
};
Navigation.js(child component)
I tried using forwardRef but seems like it only accepts one argument as ref though I have multiple refs.
const Navigation = React.forwardRef(({ handleScroll }, ref) => {
// it only accepts on ref argument
const items = [
{ id: 1, name: "Social connections", pointer: "social-connections", to: ref }, // socialRef
{ id: 2, name: "SMS preference", pointer: "sms", to: ref }, // smsRef
];
return (
<>
<Box>
<UL>
{items.map(item => {
return (
<LI
key={item.id}
active={item.active}
onClick={() => handleScroll(item.to)}
>
{item.name}
</LI>
);
})}
</UL>
</Box>
</>
);
});
export default Navigation;
Form.js
I do not have idea on passing multiple refs when using forwardRef so for form section I have passed the refs as simple props passing.
const Form = ({ socialRef, smsRef }) => {
return (
<>
<Formik initialValues={initialValues()}>
{({ handleSubmit }) => {
return (
<form onSubmit={handleSubmit}>
<Social socialRef={socialRef} />
<SMS smsRef={smsRef} />
</form>
);
}}
</Formik>
</>
);
};
Social.js
const Social = ({ socialRef }) => {
return (
<>
<Row ref={socialRef}>
<Col xs={12} md={3}>
<Label>Social connections</Label>
</Col>
<Col xs={12} md={6}></Col>
</Row>
</>
);
};
Can anyone help me at passing multiple refs so when clicked on the particular navigation item, it should scroll me to its respective component(section).
I have added an example below. I have not tested this. This is just the idea.
import React, { createContext, useState, useContext, useRef, useEffect } from 'react'
export const RefContext = createContext({});
export const RefContextProvider = ({ children }) => {
const [refs, setRefs] = useState({});
return <RefContext.Provider value={{ refs, setRefs }}>
{children}
</RefContext.Provider>;
};
const Profile = ({ children }) => {
// ---------------- Here you can access refs set in the Navigation
const { refs } = useContext(RefContext);
console.log(refs.socialRef, refs.smsRef);
return <>
{children}
</>;
};
const Navigation = () => {
const socialRef = useRef(null);
const smsRef = useRef(null);
const { setRefs } = useContext(RefContext);
// --------------- Here you add the refs to context
useEffect(() => {
if (socialRef && smsRef) {
setRefs({ socialRef, smsRef });
}
}, [socialRef, smsRef, setRefs]);
return <>
<div ref={socialRef}></div>
<div ref={smsRef}></div>
</>
};
export const Example = () => {
return (
<RefContextProvider>
<Profile>
<Navigation />
</Profile>
</RefContextProvider>
);
};
I am trying to create a reusable carousel using react-bootstrap, i could create that one"
const ReusableCarousel = (props) => {
return (
<Carousel className="mt-4 border">
{props.items.map((item, index) => {
return (
<Carousel.Item key={index}>
<Row>
{props.children}
</Row>
</Carousel.Item>
);
})}
</Carousel>
);
}
Now as you see it is reusable till the point of the carousel item, props.children may represent multiple elements per one slide or single element per slide, but i can not achieve that according to my logic
in the parent:
<ReusableCarousel items={categories}> //categories is an array of arrays of objects
{
//item prop should be passed here to carouselItemCategories component
//but i couldn't find a way to achieve that
<CarouselItemCategories key={i} />
}
</ReusableCarousel>
carouselItemCategories Component:
const CarouselItemCategories = (props) => {
//still in my dreams
const { item } = props;
return (
<>
{
item.map((c, index) => {
return (
<Col key={index}>
//a category card here
</Col>
);
})
}
</>
);
}
Now i know what makes it work, it is about passing item prop(which represent an array of objects represents fraction of my categories) but i could not find any way to achieve that
you can imagine categories like that:
const categories = [
[
{
title: 'Laptops',
background: 'red'
},
{
title: 'Tablets',
background: 'blue';
}
],
[
{
title: 'Mouses',
background: 'yellow'
},
{
title: 'Printers',
background: 'orange';
}
]
]
If I understand you correctly, you want to use each of the items from your ReusableCarousel to generate a new CarouselItemCategories with the individual item passed in as a prop?
If so, you may want to take a look at the cloneElement function. Effectively, inside your mapping of the items prop, you would create a clone of your child element, and attach the individual item as a prop to that clone. Something like this:
const ReusableCarousel = (props) => {
return (
<Carousel className="mt-4 border">
{props.items.map((item, index) => {
return (
<Carousel.Item key={index}>
<Row>
{React.cloneElement(props.children, { item })}
</Row>
</Carousel.Item>
);
})}
</Carousel>
);
}
I just found another solution by using react context, i created a CarouselContext module :
import React from 'react';
const CarouselContext = React.createContext([]);
export const CarouselProvider = CarouselContext.Provider;
export default CarouselContext
and then in the ReusableCarousel component:
import { CarouselProvider } from './carouselContext'
const ReusableCarousel = (props) => {
return (
<Carousel >
{props.items.map((item, index) => {
return (
<Carousel.Item key={index} >
<Row >
{
<CarouselProvider value={item}>
{props.children}
</CarouselProvider>
}
</Row>
</Carousel.Item>
);
})}
</Carousel>
);
}
and then using the context to get item global variable
const CarouselItemCategories = () => {
const item = useContext(CarouselContext);
return (
<>
{
item.map((c, index) => {
return (
<Col>
//category card here
</Col>
);
})
}
</>
);
}
I want to make a page with react components.
For that, I want to separate my code in several parts.
I want to create a page in my Layout function that uses various components like my MenuComponent.
However, I do not understand how to recover my MenuComponent in my Layout.
Thanks for your help.
function Menu () {
const [menuItems, setItems] = useState(null);
useEffect(() => {
getMenuItems().then(setItems);
}, []);
if (!menuItems) {
return null;
}
return (
<div>
{menuItems.data.root.children.map(menu => {
return (
<TreeItem key={menu.id} nodeId="1" label={menu.text} labelIcon={Label}>
{menu.children.map(child => {
return (
<TreeItem key={child.id} nodeId="1" label={child.text} labelIcon={Label}>
{console.log(child.text)}
</TreeItem>
);
})}
</TreeItem>
);
})}
</div>
);
}
export default function Layout() {
const classes = useStyles();
if (!isLogged()) {
return (
<div className="fondLogin" fullscreen>
<SnackbarProvider
maxSnack={1}
anchorOrigin={{
vertical: "top",
horizontal: "center"
}}
>
<Login onSuccess />
</SnackbarProvider>
</div>
);
}
return (
<div className="containerGeneral">
<InactivityManager />
<Menu />
</div>
);
}
Satif is completely right, if you are using a hooks approach, then you should make an async calls into useEffect hook.
Another thing, your component should always return something. If you want to prevent render just return null. In your case you are returning undefined.
function Menu () {
const [menuItems, setItems] = useState(null);
useEffect(() => {
getMenuItems().then(setItems);
}, []);
if (!menuItems) {
return null;
}
return (
<div>
{menuItems.children.map(menu => {
return (
<TreeItem key={menu.id} nodeId="1" label={menu.text} labelIcon={Label}>
</TreeItem>
);
})};
</div>
);
}
The menu component is wrong. You need to fetch a data in useEffect or componentDidMount methods.