If else within a map - javascript

Each of the user relations object looks like this:
"userRelations": [
{
"relatedUser": {
"id": 4,
"firstName": "Jack",
"lastName": "Miller"
},
"type": "FRIEND"
},
{
"relatedUser": {
"id": 3,
"firstName": "Rhena",
"lastName": "Tahoma"
},
"type": "CONTACT"
}
]
Currently, my code renders all relatedUsers. However, I only want to render those which have "type": "contact". Is it
possible to check for this condition within the return
type RelatedUser = {
firstName: string,
lastName: string,
id: string,
phoneNumber: string,
};
type RelationType = {
type: string,
};
export const ContactList: React.FunctionComponent<UserProps> = ({ data }) => {
if (!data) return null;
return (
<View style={styles.users}>
{data.users.nodes[0].userRelations.map(
(item: { relatedUser: RelatedUser, type: RelationType}) => {
const userName = item.relatedUser.firstName.concat(' ').concat(item.relatedUser.lastName);
// if (item.type: "CONTACT"){
return (
<View style={styles.item} key={item.relatedUser.id}>
<Text style={styles.userName}>{userName}</Text>
</View>
);
},
)}
</View>
);
};

Instead of doing that inside map's return I would suggest to chain .filter() with map():
userRelations.filter(({ type }) => type === 'CONTACT').map(/* your code*/)

Related

Displaying data from API undefined

I'm trying to display student data and the courses they are enrolled in from a a simple API I made.
Here's the result from the API https://localhost:44309/api/Students
[
{
"id": 1,
"lastName": "Rizal",
"firstName": "Jose",
"courses": [
"C#",
"Javascript",
"CSS"
]
},
{
"id": 2,
"lastName": "Bonifacio",
"firstName": "Andres",
"courses": [
"HTML",
"ASP.NET MVC"
]
},
{
"id": 3,
"lastName": "Sora",
"firstName": "Tandang",
"courses": [
"CSS",
".NET"
]
}
]
Here's my Javascript code
import React from "react"
import { useState, useEffect } from "react";
const StudentList = () => {
const [students, setStudent] = useState([]);
const getData = async () => {
const response = await fetch("https://localhost:44309/api/students");
const data = await response.json()
setStudent(data)
}
useEffect(() => {
getData()
}, []);
return (
<div className="student-list">
<h2>Students and their courses</h2>
{students.map(student => (
<div className="student-preview" key={student.id}>
<p>{ student.fname } { student.lname }</p>
<p>{ student.courses }</p>
</div>
))}
</div>
)
}
export default StudentList
I'm getting an error saying
Uncaught ReferenceError: students is not defined
I wonder what I'm doing wrong.
Something is wrong with your api the code works fine with static data
import React from "react";
import { useState, useEffect } from "react";
const StudentList = () => {
const [students, setStudent] = useState([
{
id: 1,
lastName: "Rizal",
firstName: "Jose",
courses: ["C#", "Javascript", "CSS"]
},
{
id: 2,
lastName: "Bonifacio",
firstName: "Andres",
courses: ["HTML", "ASP.NET MVC"]
},
{
id: 3,
lastName: "Sora",
firstName: "Tandang",
courses: ["CSS", ".NET"]
}
]);
return (
<div className="student-list">
<h2>Students and their courses</h2>
{students.map((student) => (
<div className="student-preview" key={student.id}>
<p>
{student.fname} {student.lname}
</p>
<p>{student.courses}</p>
</div>
))}
</div>
);
};
export default StudentList;

Custom Strapi field shows null

Hi everyone,
I have created a custom field plugin which is connectect to mapbox search API to get locations when the user types.
I have created the component and everything seems to be working fine until the part where I need to send the object to API.
When I want to store the location title in the API it works just fine
{
"id": 1,
"companyName": "Stina",
"location": "South Africa",
"published_at": "2021-02-25T22:31:14.550Z",
"created_at": "2021-02-25T22:29:18.335Z",
"updated_at": "2021-02-28T21:55:15.064Z",
}
So this is the code used to get that:
const updateLocationValue = (locationValue) => {
props.onChange({
target: { name: "location", value: locationValue.title },
});
};
But when I want to send the whole object so I can get the coordinates as well I get null
{
"id": 1,
"companyName": "Stina",
"location": null,
"published_at": "2021-02-25T22:31:14.550Z",
"created_at": "2021-02-25T22:29:18.335Z",
"updated_at": "2021-02-28T21:55:15.064Z",
}
This is the code that I use to send the whole object, and this works just fine in the console log and I am getting the coordinates as well but I can't pass it to Strapi:
const updateLocationValue = (locationValue) => {
props.onChange({
target: { name: "location", value: locationValue },
});
};
I am expecting to see an object just as below so I can get the title and coordinates:
{
"id": 1,
"companyName": "Stina",
"location": {object},
"published_at": "2021-02-25T22:31:14.550Z",
"created_at": "2021-02-25T22:29:18.335Z",
"updated_at": "2021-02-28T21:55:15.064Z",
}
I have registered the field in company.settings.json just as you can see below:
{
"kind": "collectionType",
"collectionName": "companies",
"info": {
"name": "Company",
"description": ""
},
"options": {
"increments": true,
"timestamps": true,
"draftAndPublish": true
},
"attributes": {
"companyName": {
"type": "string"
},
"location": {
"type": "locationsearch",
"columnType": "longtext"
},
"products": {
"collection": "product",
"via": "company"
}
}
}
So the columnType is longtext, I have tried to change it to "object" or "JSON" but it still shows me null
Here is the full code of the component which is working just fine:
import { Label, InputText } from "#buffetjs/core";
const apiToken =
"****************************************";
export default function Location(props) {
const [locationTitle, setLocationTitle] = useState("");
const [suggestions, setSuggestions] = useState([]);
const [suggestionsOpen, setSuggestionsOpen] = useState(false);
const wrapperRef = useRef(null);
const popperEl = useRef(null);
const [cursor, setCursor] = useState(null);
const fetchLocation = (searchText) => {
searchText &&
fetch(
`https://api.mapbox.com/geocoding/v5/mapbox.places/${searchText}.json?access_token=${apiToken}`
)
.then((response) => response.json())
.then((data) => {
const newSuggestions = data
? data.features.map((feature) => ({
title: feature.place_name,
centerCoordinates: feature.center,
}))
: [];
setSuggestions(newSuggestions);
});
};
useEffect(() => {
locationTitle && fetchLocation(locationTitle);
}, [locationTitle]);
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
});
const updateLocationValue = (locationValue) => {
props.onChange({
target: { name: "location", value: locationValue.title },
});
};
const handleClickOutside = (e) => {
const { current: wrap } = wrapperRef;
if (wrap && !wrap.contains(e.target)) {
setSuggestionsOpen(false);
}
};
const setLocationValue = (location) => {
setLocationTitle(location.title);
updateLocationValue(location);
setSuggestionsOpen(false);
};
return (
<div ref={wrapperRef}>
<Label htmlFor="input">Location</Label>
<InputText
name="input"
onChange={(e) => setLocationTitle(e.target.value)}
placeholder="Riva 16, 21420, Bol, Croatia"
type="search"
value={locationTitle}
onClick={() => setSuggestionsOpen(true)}
/>
<div ref={popperEl} className="options-wrapper">
{suggestionsOpen && (
<div>
{suggestions.map((suggestion) => {
return (
<div
onClick={() => setLocationValue(suggestion)}
key={suggestion.id}
>
{suggestion.title}
</div>
);
})}
</div>
)}
</div>
</div>
);
}
Could anyone recommend any steps to finalize this?
Thanks a lot!

Save data in a specific way

I have an application where user have to input data about it and after saving that form, the data should be outputed in a certain form.
const ParrentForm = () => {
const [carNr, setCarNr] = useState([]);
const onFinish = (values) => {
const { firstName, lastName } = values;
const user = {
firstName,
lastName,
things: {
cars: []
}
};
console.log(user);
};
const onFinishFailed = (errorInfo) => {
console.log("Failed:", errorInfo);
};
const setFloorsNrHandler = (nr) => {
setCarNr(Array.from({ length: nr }, (v, k) => k));
};
return (
<div>
<Form
name="main"
initialValues={{ remember: true }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
>
<Form.Item
label="First Name"
name="firstName"
rules={[{ required: true, message: "Please input your first name!" }]}
>
<Input />
</Form.Item>
<Form.Item
label="Last Name"
name="lastName"
rules={[{ required: true, message: "Please input your last name!" }]}
>
<Input />
</Form.Item>
<Form.Item
name="nrOfCars"
label="Cars"
rules={[{ type: "number", min: 0, max: 15 }]}
>
<InputNumber onChange={setFloorsNrHandler} />
</Form.Item>
{carNr.map((f, k) => {
return (
<Form.Item key={k}>
<InnerForm cKey={k} />
</Form.Item>
);
})}
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
</div>
);
};
export default ParrentForm;
The expected data should look like this:
{
"user": {
"firstName": "Bill",
"lastName": "M.",
},
"things": {
"cars": [
{
"nr": 1,
"carsList": [
{
"name": "Car 1",
"nr": 1
},
{
"name": "Audi",
"nr": 2
},
,
{
"name": "Mercedes",
"nr": 3
}
]
},
{
"nr": 2,
"carsList": [
{
"name": "Bmw",
"nr": 1
}
]
}
]
}
}
How to modify the data inside onFinish function to get the result above?
demo: https://codesandbox.io/s/modest-cache-o0g65?file=/OuterForm.js:136-1755
First you have to destruct (doc) the form values object combining with spread operator (doc) to get the car-related values (I stored in variable carValues). After that, the only things left to do is manipulate with that variable, here I combined the use of Object.entries and Array.prototype.map
There a part with Number(key), because Object.entries() transforms object in to array of key-value pair, with key's type of string, so you have to cast it to Number first for 1-based index
const onFinish = values => {
const { firstName, lastName, nrOfCars, ...carValues } = values
const cars = Object.entries(carValues).map(([key, value]) => ({
nr: Number(key) + 1,
carsList: (value.cars || []).map((car, carIndex) => ({
nr: carIndex + 1,
name: car.name
}))
}))
const user = {
firstName,
lastName,
things: {
cars
}
}
console.log(JSON.stringify(user, null, 2))
}
Forked codesandbox for implementation

.filter with a map in Typescript

I am using data that is returned via a graphql query. Each of the user relations object looks like this:
"userRelations": [
{
"relatedUser": {
"id": 4,
"firstName": "Jack",
"lastName": "Miller"
},
"type": "FRIEND"
},
{
"relatedUser": {
"id": 3,
"firstName": "Rhena",
"lastName": "Tahoma"
},
"type": "CONTACT"
}
]
Currently, my code renders all relatedUsers regardless of what their typeis. However, I only want to render those which have "type": "contact". I am trying to use the . filterfunction but I am unable to do so.
type UserProps = {
data: UsersLazyQueryHookResult;
};
type RelatedUser = {
firstName: string,
lastName: string,
id: string,
phoneNumber: string,
};
type RelationType = {
type: string,
};
export const ContactList: React.FunctionComponent<UserProps> = ({ data }) => {
if (!data) return null;
return (
<View style={styles.users}>
{data.users.nodes[0].userRelations.map(
(item: { relatedUser: RelatedUser, type: RelationType}) => {
// if (item.type: "CONTACT"){
return (
<View style={styles.item} key={item.relatedUser.id}>
...
</View>
</View>
);
},
)}
</View>
);
};
I tried using this: data.users.nodes[0].userRelations.filter(({ type }) => type === 'CONTACT').map(/*code*/)
but it gives me an error that:
Binding element 'type' implicitly has an 'any' type.ts(7031)
If I add : RelationType infront of type, I get 'RelationType' is declared but its value is never read.ts(6133) Binding element 'RelationType' implicitly has an 'any' type
What else can I do to filter out and map/render only CONTACT types.

How to get objects in array of object in React Native

I'm kind of lost to access some info in my static data. Here's the data :
{
"info1": {
"label": "label",
"class": "class-css",
"title": "title",
"text": "text",
"number": "20",
"tags": [
{
"name": "#twitter"
},
{
"name": "#myspace"
}
]
},
"info2": {
"label": "label",
"class": "class-css",
"title": "title",
"text": "text",
"number": "20",
"tags": [
{
"name": "#instagram"
},
{
"name": "#facebook"
}
]
}
}
Then I get the first info like that :
this.setState({
currentLabel: this.state.labels["info1"]
})
This is why I want and then I want to display info in a component and it's working until I try to get tags information. I tried a .map() but without success and error.
<View>
<Text>{infoDetail.title}</Text>
<Text>{infoDetail.text}</Text>
<Text>How do I get "tags" information</Text>
</View>
Is it possible to access these objects in the array "tags" ?
yes you can call tags as follows infoDetail.tags and do map on it
render(){
const tagItems = infoDetail && infoDetail.tags.map((item, index) => {
return <Text key={index}>{item.name}</Text>
});
return(
<View>
<Text>{infoDetail.title}</Text>
<Text>{infoDetail.text}</Text>
{tagItems}
</View>
)
}
Here is a full working code. Since your labels state property is an object, you need to map it somehow. I've chosen Object.values here. You can use Object.keys or even Object.entries according to your needs.
I've used a separate Info component and passed the values to it, then render there. In this component, we are again mapping the tags, then rendering the list.
class App extends React.Component {
state = {
labels: {
info1: {
label: "label1",
class: "class-css",
title: "title",
text: "text",
number: "20",
tags: [
{
name: "#twitter",
},
{
name: "#myspace",
},
],
},
info2: {
label: "label2",
class: "class-css",
title: "title",
text: "text",
number: "20",
tags: [
{
name: "#instagram",
},
{
name: "#facebook",
},
],
},
},
}
render() {
const { labels } = this.state;
return (
<div>
{
Object.values( labels ).map( value =>
<Info label={value} key={value.label} /> )
}
</div>
);
}
}
const Info = ( props ) => {
const { title, text, tags } = props.label;
const tagList = tags.map( tag => <p key={tag.name}>{tag.name}</p> );
return (
<div style={{ border: "1px solid gray", marginTop: "-1px" }}>
<p>{title}</p>
<p>{text}</p>
<div>{tagList}</div>
</div>
);
};
ReactDOM.render(
<App />,
document.getElementById("root")
);
<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="root"></div>
Update
If your data is totally static then #Xavi A.'s method is a good option. I don't know how is your list but I provide a simple code including something like you want here.
const labels = {
info1: {
label: "label1",
class: "class-css",
title: "title",
text: "text",
number: "20",
tags: [
{
name: "#twitter"
},
{
name: "#myspace"
}
]
},
info2: {
label: "label2",
class: "class-css",
title: "title",
text: "text",
number: "20",
tags: [
{
name: "#instagram"
},
{
name: "#facebook"
}
]
}
};
class App extends React.Component {
state = {
currentLabel: Object.keys(labels)[0]
};
handleInfoChange = info => this.setState({ currentLabel: info });
renderList = () => (
<ul>
{Object.keys(labels).map(info => (
<Item key={info} info={info} onClick={this.handleInfoChange} />
))}
</ul>
);
render() {
const { currentLabel } = this.state;
return (
<div>
{this.renderList()}
<Info currentLabel={currentLabel} />
</div>
);
}
}
const Item = props => {
const { info, onClick } = props;
const handleClick = () => onClick(info);
return <li onClick={handleClick}>{info}</li>;
};
const Info = props => {
const { currentLabel } = props;
const { title, text, tags } = labels[currentLabel];
const tagList = tags.map(tag => <p key={tag.name}>{tag.name}</p>);
return (
<div style={{ border: "1px solid gray", marginTop: "-1px" }}>
<p>{title}</p>
<p>{text}</p>
<div>{tagList}</div>
</div>
);
};
ReactDOM.render( <App />, document.getElementById( "root" ) );
<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="root"></div>
Probably something like this.
<Text>{infoDetail.tags.map(tag => {/*render */})}</Text>
You can try Object.keys() and Array.prototype.reduce() to get your favorite data:
const data = {
"info1": {
"label": "label",
"class": "class-css",
"title": "title",
"text": "text",
"number": "20",
"tags": [
{
"name": "#twitter"
},
{
"name": "#myspace"
}
]
},
"info2": {
"label": "label",
"class": "class-css",
"title": "title",
"text": "text",
"number": "20",
"tags": [
{
"name": "#instagram"
},
{
"name": "#facebook"
}
]
}
};
const tags = Object.keys(data).reduce((result, key) => {
return result.concat(data[key].tags);
}, [])
console.log(tags);
/* tags = [
{
"name": "#twitter"
},
{
"name": "#myspace"
},
{
"name": "#instagram"
},
{
"name": "#facebook"
}
] */
No need to save all the static data in your state, you can keep your state cleaner by just saving the selected label:
onLabelSelect = label => {
//label will be "info1" for example
this.setState({
currentLabel: label
})
}
Then in your render:
render(){
//get infoDetail from staticData
const infoDetail = staticData[this.state.currentLabel]
return (
<View>
<Text>{infoDetail.title}</Text>
<Text>{infoDetail.text}</Text>
{infoDetail.tags.map( ({name}) => <Text>name</Text>)}
</View>
)
}
Note about the map. This:
{infoDetail.tags.map( ({name}) => <Text>name</Text>)}
is a shorter version of:
{infoDetail.tags.map( item => {
return <Text>item.name</Text>
})}

Categories