I'm trying to pass an array of objects to a function on react, I'm getting an error of Uncaught TypeError: props.map is not a function although when I print the props, namely users, to the log before passing it on, it is an array
I have the following data:
{
"name": "My test name",
"users": [
{
"name": "user 1 name",
"data": [
{
"var1": [],
"var2": {
"sub1": "open",
"sub2": ""
},
},
{
"var1": [],
"var2": {
"sub1": "close",
"sub2": ""
},
},
]
}
]}
The data is received by a REST API call.
I'm trying to process it using the following react code:
interface SingleUser {
var1: string[];
var2: {
sub1: string;
sub2: string;
}
}
interface Users {
name: string;
users:SingleUser[];
}
render() {
const { name, users} = this.state;
return (
<div className='flow d-flex flex-row flex-wrap justify-content-start'>
<GenInfo name={name} />
<MyTabs {...users}/>
</div>
);}
function MyTabs(props: Users[]) {
const data = props
const tabs = props.map((item) => {
return {
tabId: item.name.replace(/ /g,"_"),
label: item.name,
content: <TestTable {...item.users} />
}
});
return <HorizontalTabs tabs={tabs} pills />
}
You can define props ò MyTabs as data like this
render() {
const { name, users} = this.state;
return (
<div className='flow d-flex flex-row flex-wrap justify-content-start'>
<GenInfo name={name} />
<MyTabs data={users}/>
</div>
);}
function MyTabs({data: Users[]}) {
const tabs = data.map((item) => {
return {
tabId: item.name.replace(/ /g,"_"),
label: item.name,
content: <TestTable {...item.users} />
}
});
return <HorizontalTabs tabs={tabs} pills />
}
Related
when user come to my website, I want him to choose group by clicking proper button, then filter all products by checking if in groups products_bound is the matching item. It's not working... I think I missed something in showProduct function, Im new and still learning please tell me if I'm doing something in bad way or what could be better in my code.
My dummy state in file:
products = [
{
id: 1,
title: Apple,
price: 3.99,
},
{
id: 2,
title: Banana,
price: 5.99,
},
{
id: 3,
title: Carrot,
price: 1.99,
},
];
groups = [
{
name: Fruits,
products_bound: [
{
id: 1,
title: Apple,
price: 3.99,
},
{
id: 2,
title: Banana,
price: 5.99,
},
],
},
];
ChooseGroup.js
function ChooseGroup({ groups, showProductsButton }) {
return (
<div>
{groups.map((group) => {
return (
<button
id={group.name}
onClick={(e) => {
showProductsButton(e.target.id);
}}
>
{group.name}
</button>
);
})}
</div>
);
}
export default ChooseGroup;
index.js
function Homepage(props) {
const [filteredProductsByGroup, setFilteredProductsByGroup] = useState();
const [isThisGroupActive, setThisGroupActive] = useState(false);
function showProducts(clickedButtonId) {
const checkWhichGroupIsClicked = groups.filter(
(single) => single.name === clickedButtonId
);
const unpackGroup = checkWhichGroupIsClicked.map((single) =>
single.products_bound.map((i) => i)
);
const filter = products.filter((element) => {
unpackGroup.some((group) => {
return group == element.title;
});
});
return setFilteredProductsByGroup(filter), setThisGroupActive(true);
}
return (
<>
<Header />
<ChooseGroup groupList={props.groups} showProductsButton={showProducts} />
{isThisGroupActive ? (
filteredProductsByGroup.map((group) => {
return <Products groupDetails={group} />;
})
) : (
<div>Select group!</div>
)}
</>
);
}
i have a file contain objects data.js.
components: [
{
id: "1",
nameC: "name",
type: "TextInput",
options: { placeholder: "saisir nom", required: true },
},
{
id: "2",
nameC: "Phone",
type: "Phone",
options: { placeholder: "saisir number", required: false },
},
{
id: "3",
nameC: "name",
type: "TextInput",
options: { placeholder: "saisir nom", required: true },
},
i got those objects to create inputs Dynamically in this file TemplateScreen.js .
<>
{getData.length === 0 ? (
<Empty />
) : (
getData.map((item, index) => {
switch (item.type) {
case "TextInput":
return (
<>
<InputText
ModuleName={item.nameC}
placeholder={item.options.placeholder}
required={item.options.required}
/>
</>
);
case "Phone":
return (
<>
<Phone
ModuleName={item.nameC}
placeholder={item.options.placeholder}
required={item.options.required}
/>
</>
);
default:
return <Text>Nothing hear</Text>;
}
})
)}
</>
i render inputs successfully but i'can't handle those inputs :( .i'm tried many methods but anyone worked for me.i am tried many tricks from reactjs tuto but nothing worked for me .i'm blocked 4 days in this problem,please anyone can help me :(
this file contain TextInput component, i am called it in templateScreen.js
Phone component is the same as TextInput component with a bit of difference
export const InputText = (props) => {
const [state, setState] = React.useState("");
return (
<View style={styles.container} key={props.keys}>
<View style={styles.Namecontainer}>
<Text style={styles.moduleName}>{props.ModuleName}</Text>
{props.required ? <Text style={styles.required}>*</Text> : <></>}
</View>
<TextInput
{...props}
value={state}
onChangeText={(text) => setState(text)}
placeholder={props.placeholder}
style={styles.inputtext}
/>
</View>
);
};```
Instead of placing state inside the Input text and Phone text component, why not just use useRef hook inside the template screen js? We should generate refs depending on the length of the data, thus
const inputRefs = getData.reduce((acc,curr)=>{
const ref = useRef(“”);
acc[curr.nameC] = ref;
return acc:
}, {});
this will generate refs for each of your inputs. Now in our map method, we just place our input refs to each ex:
<InputText
inputRefs[item.nameC]
ModuleName={item.nameC}
placeholder={item.options.placeholder}
required={item.options.required}
/>
In order to get their values, map the inputRefs and try console.log(inputRef.current) to see.
I have created the replica in reactjs because react-native is not set up on my PC but the logic remains the same, as I correctly understand what you wanna do is render dynamic input form according to the data this is provided to you and store the value in the state
import React, { useEffect, useState } from "react";
const components = [
{
id: "1",
nameC: "name",
type: "TextInput",
options: { placeholder: "saisir nom", required: true },
},
{
id: "2",
nameC: "phone",
type: "Phone",
options: { placeholder: "saisir number", required: false },
},
{
id: "3",
nameC: "city",
type: "TextInput",
options: { placeholder: "saisir nom", required: true },
},
];
const DynamicInput = () => {
const [field, setField] = useState();
const handleChange = (event) => {
setField({ ...field, [event.target.name]: event.target.value });
};
useEffect(() => {
let obj = {};
components.forEach((item, index) => {
obj = { ...obj, [item.nameC]: "" };
});
setField(obj);
}, []);
console.log(field)
return (
<div>
{!field ? (
<div>Loading...</div>
) : (
components.map((item) => {
const value = field[item.nameC];
switch (item.type) {
case "TextInput":
return (
<InputText
key={item.nameC}
value={value}
onChangeHandler={handleChange}
placeholder={item.options.placeholder}
isRequired={item.options.required}
name={item.nameC}
/>
);
case "Phone":
return <div key={item.nameC}>This will be same as input</div>;
default:
return <div key={item.nameC}>Nothing hear</div>;
}
})
)}
</div>
);
};
export default DynamicInput;
export const InputText = ({
value,
onChangeHandler,
placeholder,
isRequired,
name,
}) => {
return (
<input
value={value}
name={name}
onChange={onChangeHandler}
placeholder={placeholder}
required={isRequired}
/>
);
};
The form data is set by an the data object. Need help figuring out how to update /handleChange the text inputs
I've tried unique name, those probably won't work because it wont match the "key" in the object.
Any help / input is appreciated!
Data:
export default
{
name: "Restaurants Name",
menu: [
{
category: "Appetizers",
items:
[ {
imgurl: "https://source.unsplash.com/400x200/?863127",
title: "Food 2",
desc: "",
price: "500"
},
{
imgurl: "",
title: "Food 1",
desc: "",
price: "300"
}
]
},
{
category: "Entree",
items:
[ {
imgurl: "https://source.unsplash.com/400x200/?863127",
title: "Entree 1",
desc: "",
price: "500"
},
{
imgurl: "",
title: "Entree 1",
desc: "",
price: "300"
}
]
},
]
}
Code:
import React, { useEffect, useState } from "react";
import "./../App.css"
import MenuData from "../data"
function Edit() {
const [formState, setFormState] = useState(MenuData);
useEffect(() => {
console.log(formState)
}, []);
const handleNameChange = (event) => {
const name = event.target.name;
// console.log(name)
setFormState(prevState => ({
formState: { // object that we want to update
...prevState.formState, // keep all other key-value pairs
[name]: event.target.value, // update the value of specific key
menu: {
...prevState.menu,
items: {
...prevState.menu.items
}
}
}
}))
// setFormState({
// ...formState,
// [name]: event.target.value,
// })
};
const handleChange = (categoryIndex, event) => {
// const values = [...formState]
// values[categoryIndex][event.target.name] = event.target.value;
// setFormState(values);
const name = event.target.name;
// setFormState(prevState => ({
// formState: {
// ...prevState.formState,
// menu: {
// ...prevState.menu,
// items{
// ...prevState.items
// }
// }
// }
// }));
};
return (
<div className="App">
<div>
<input name="nameField" id="nameField" maxLength="300" value={formState.name} onChange={handleNameChange} /> <br />
{formState.menu && formState.menu.map((menuitem, categoryIndex) => {
return (
<div key={categoryIndex}>
<div class="line"></div>
<h2>{menuitem.category}</h2>
<input name={"category-" + categoryIndex} id="CategoryField" maxLength="40" categoryIndex={categoryIndex} onChange={event => handleChange(categoryIndex, event)} value={menuitem.category} />
{
menuitem.items.map((item, index) => {
return(
<div key={index}>
<input name={"title-" + index + categoryIndex} id="titleField" maxLength="40" categoryIndex={categoryIndex} onChange={handleChange} value={item.title} /> <br />
<input name="desc" id="descField" maxLength="200" categoryIndex={categoryIndex} onChange={handleChange} value={item.desc} />
<br /><br />
</div>
)
})
}
</div>
)
}
)
}
</div>
</div>
);
}
export default Edit;
UPDATED
Not able to figure out the onChange function to updated nested items
I have a component, menu.js, that i import into a page to produce a list of articles, that can be filtered by category. This works perfectly.
Now i want to change the component so that i can filter the articles by tags. The problem is that the tags are a nested array in graphql, that i cant reach with the same map() function that maps the categories.
I have tried to do a nested map function but i cant get it to work, but i suspect that is the solution. My goal is to have the same functionality where i can filter the articles by tags, instead of by category. I hope thats possible. I am using gatsby, with a Strapi backend. Any hints in the right direction appreciated :-)
/src/pages/articles.js
import graphql from 'gatsby'
import React from 'react'
import Layout from 'components/layout'
import MenuBlog from 'components/menublog'
const BlogPage = ({ data }) => (
<Layout>
<MenuBlog items={data.menu} />
</Layout>
)
export default BlogPage
export const pageQuery = graphql`
query BlogQuery {
menu: allStrapiArticle {
edges {
node {
id
title
slug
tag {
title
id
}
category {
title
id
}
}
}
}
}
`
This is what i get back from the GraphQL query above, each article can of course have one or more tags, but only one category assigned
{
"data": {
"menu": {
"edges": [
{
"node": {
"title": "articleName 1",
"slug": "articleName-1",
"category": {
"title": "cat1"
},
"tag": [
{
"title": "tag1"
},
{
"title": "tag2"
},
{
"title": "tag3"
}
]
}
},
{
"node": {
"title": "articleName 2",
"slug": "articleName-2",
"category": {
"title": "cat2"
},
"tag": [
{
"title": "tag3"
}
]
}
}
]
}
}
}
And here is my component that displays the articles according to the chosen category
/src/components/menublog/index.js
import React, { Component } from 'react'
import { Link } from 'gatsby'
import Row from 'react-bootstrap/Row'
const getCategories = items => {
let tempItems = items.map(items => {
return items.node.category.title
})
let tempCategories = new Set(tempItems)
let categories = Array.from(tempCategories)
categories = ['all', ...categories]
return categories
}
export default class MenuBlog extends Component {
constructor(props) {
super(props)
this.state = {
items: props.items.edges,
articles: props.items.edges,
categories: getCategories(props.items.edges),
}
}
handleItems = category => {
let tempItems = [...this.state.items]
if (category === 'all') {
this.setState(() => {
return { articles: tempItems }
})
} else {
let items = tempItems.filter(
({ node }) => node.category.title === category
)
this.setState(() => {
return { articles: items }
})
}
}
render() {
if (this.state.items.length > 0) {
return (
<Row>
{/* items */}
<div className="col-md-8 blog-main bg-light">
<h1>Artikler</h1>
{this.state.articles.map(({ node }) => {
return (
<div key={node.id} className="blog-post mb-4">
<h2>
<Link to={`/artikler/${node.slug}`}>{node.title}</Link>
</h2>
{/* item text */}
</div>
)
})}
</div>
{/* categories */}
<div className="col-md-4 blog-sidebar">
<div className="p-4 mb-3 bg-light">
<h4>Kategorier</h4>
<ol className="list-unstyled mb-0">
{this.state.categories.map((category, index) => {
return (
<li key={index}>
<button
type="button"
className="btn"
onClick={() => {
this.handleItems(category)
}}
>
{category}
</button>
</li>
)
})}
</ol>
</div>
<div className="p-4 mb-3 bg-light">
<h4>Kategorier</h4>
</div>
</div>
</Row>
)
} else {
return <h1>no items</h1>
}
}
}
You should be able to use something similar to your category method:
items = tempItems.filter(({ node }) =>
node.tag.map(tag => tag.title).includes("tag2")
);
Since this isn't necessarily React / Gatsby specific, here is only the data and these methods:
const data = {
data: {
menu: {
edges: [{
node: {
title: "articleName 1",
slug: "articleName-1",
category: {
title: "cat1"
},
tag: [{
title: "tag1"
},
{
title: "tag2"
},
{
title: "tag3"
}
]
}
},
{
node: {
title: "articleName 2",
slug: "articleName-2",
category: {
title: "cat2"
},
tag: [{
title: "tag3"
}]
}
}
]
}
}
};
let items = data.data.menu.edges.filter(
({
node
}) => node.category.title === "cat2"
);
console.log(items);
items = data.data.menu.edges.filter(({
node
}) =>
node.tag.map(tag => tag.title).includes("tag2")
);
console.log(items);
While passing the array to the component I am getting the error-'this.props.tasks.map is not a function'
My Main.js code
let posts = [
{
id: 1,
description: "This is a task",
status: "pending"
},
{
id: 2,
description: "This is another task",
status: "pending"
},
{
id: 3,
description: "This is an easy task",
status: "pending"
}
];
class Main extends Component {
render() {
return <div>
<Title title="Photowall" />
<Photowall posts={ posts } />
</div>
}
}
and the Photowall.js code
render() {
return <div>
{this.props.posts.map((item) => <Photo key={item} post={item}/>)}
</div>
}
You have to pass posts like below.
<Photowall posts={posts} />
Photowall.js You have to pass key={item.id} and I guess this will work.
render() {
return <div>
{this.props.posts.map((item) => <Photo key={item.id} post={item}/>)}
</div>
}
Photo
class Photo extends Component {
render() {
const data = this.props.post;
return <p>{data.description}</p>
}
}
If you are passing like {{ posts }} then it will be consider as below at other end.
{
posts: [
{
id: 1,
description: "This is a task",
status: "pending"
},
{
id: 2,
description: "This is another task",
status: "pending"
},
{
id: 3,
description: "This is an easy task",
status: "pending"
}
]
}
So that's why this will not work.
Hope this will work for you!
I think you meant to write
<Photowall posts={ posts } />
Actually you are passing Object of array by passing posts={{posts}}.
Only pass this,
<Photowall posts={ posts } />
Demo