How to map dictionary in react class component - javascript

Hello, community! Hope you all are good.
I was trying to map a dictionary in my class component but it gives me a hooks error.
I wanted to create several cards with different information so that I approached like this but now it shows me the Hooks error and I tried many things and find out that this piece of code has an error but can't figure out what please help me.
My code is below
export class AskModal extends Component {
constructor(props) {
super(props);
this.state = {
modalState: true,
services: {
"Employement": [
"/employement",
"/images/humhai-static-data/employment.jpg",
"This para is for Employement",
"text-dark"
],
"Business opportunity": [
"/self-employement",
"/images/humhai-static-data/buisness-opportunity.jpg",
"This para is for Self Employement",
"text-dark"
],
"Covid Helpdesk": [
"../covid-helpline",
"/images/humhai-static-data/covid-helpdesk.jpg",
"This para is for Covid help desk",
"text-danger"
],
"Volunteer": [
"../covid-helpline/volunteer",
"/images/humhai-static-data/volunteer.jpg",
"This para is for Volunteer",
"text-dark"
],
},
};
this.toggle = this.toggle.bind(this);
}
toggle = () => {
this.setState({
modalState: !this.state.modalState
});
}
render() {
return (
<>
{Object.keys(this.state.services).map(name => (
<MDBCol md="3" size="6" className="mx-auto my-2">
<MDBCard className="border rounded h-100 z-depth-5" color={this.state.services[name][3]}>
<Link to={this.state.services[name][0]} onClick={this.toggle} className={this.state.services[name][3]}>
<MDBCardImage className="img-fluid" src={this.state.services[name][1]} />
<MDBCardBody>
<MDBCardTitle><i className="fa fa-chevron-right"></i> {name}</MDBCardTitle>
<div className="d-none d-lg-block">
<hr />
<MDBCardText>
<p>{this.state.services[name][2]}</p>
</MDBCardText>
</div>
</MDBCardBody>
</Link>
</MDBCard>
</MDBCol>
))
}
</>
);
}
}

I think the error is because you are doing a map on this.state.services and it ideally shouldn't even be related to hooks as you are not using them here in this component.
If you check your state object, this.state.services would return an object and not an array. So, if you could revisit your code and check the same.
Also, the way you have created the services object might not be the best way, just putting a snippet below if it helps as to how you can make it more scalabale.
const services = [
{ name: "Employment", route: "/employment", image: "path/to/image.jpg", text: "Text", mode: "dark mode" }, // and so on
]

Related

Can't access an array inside an Object after passed props to a modal component

i'm building this application with the help of the RestCountries Api to be able to show each country with basic details on a grid, and after a click on each box the app will show a modal with more detailed informations. That's my code so far:
class App extends React.Component{
constructor (props){
super (props);
this.state={
countries : [],
clickedCountry: {},
modalOn : false,
}
}
componentDidMount(){
axios.get(`https://restcountries.eu/rest/v2/all`)
.then(res => {
const data = res.data;
this.setState({
countries : data
})
let countries = this.state.countries
console.log(countries);
})
}
showInfo = (name) => {
this.setState({
clickedCountry : this.state.countries.find(it => it.name===name),
modalOn : true
});
}
closeModal =()=>{
this.setState({
modalOn : false
})
}
render() {
return (
<div className="container">
{this.state.countries.map(country=>
<Country name={country.name}
key={country.name}
population ={country.population}
region={country.region}
capital={country.capital}
flag={country.flag}
showInfo={this.showInfo}
languages={country.languages}
/>
)}
<div style={{display: this.state.modalOn? "block" : "none"}}>
<Modal closeModal={this.closeModal}
name={this.state.clickedCountry.name}
population={this.state.clickedCountry.population}
region={this.state.clickedCountry.region}
capital ={this.state.clickedCountry.capital}
flag={this.state.clickedCountry.flag}
nativeName ={this.state.clickedCountry.nativeName}
subregion={this.state.clickedCountry.subregion}
topLevelDomain={this.state.clickedCountry.topLevelDomain}
languages={this.state.clickedCountry.languages}
/>
</div>
</div>
)
}
}
Modal component :
const Modal = ({closeModal, name, population, region, capital, flag, languages, nativeName, subregion, topLevelDomain, currencies}) => {
return (
<div className="modal">
<div className="modal-content">
<span onClick={closeModal}>x</span>
<div className="img">
<img src={flag}/>
</div>
<p>{name}</p>
<p>Native name: {nativeName}</p>
<p>population: {population}</p>
<p>Region: {region}</p>
<p>Sub Region: {subregion}</p>
<p>Top level domain: {topLevelDomain}</p>
<p>Capital: {capital}</p>
</div>
</div>
)
}
So far for now i have mapped each country and the modal on click is showing more detailed informations. The problem now is the fact i need to access in the api an array that is nested inside an object:
area: 91
gini: null
timezones: ["UTC-04:00"]
borders: []
nativeName: "Anguilla"
numericCode: "660"
currencies: [{…}]
languages: [{…}]
translations: {de: "Anguilla", es: "Anguilla", fr: "Anguilla", ja: "アンギラ", it: "Anguilla", …}
flag: "https://restcountri
I need to access the languages array. Now if i try to map languages inside the country component, i can display the informations. But i want to show the langauges only on the modal component, and if i map the clickedCountry state responsible for the modal i will receive the error that "languages" is undefined. How it comes if is the same object filtered through the find function? Hope i was clear guys, cheers.
I know you understood whats happened!, just add this to Modal component
<ul>
{
languages && languages.map(lan=> {return <li>{lan.name}</li>} )
}
</ul>

is there a way to query data based on a particular item from a json object?

I have user interface which looks should look like this
and that picture above is just pure HTML.
So when I tried to create it with React, I am failing to align the tv shows with a particular TV channel overflowing horizontally as per the channel.
Picture of what I get in React
I am querying the data from json files that have the objects and the TV channel object looks like
{
"groupID": 16481,
"hasMediathek": true,
"storeUrlAndroid": null,
"storeUrlApple": null,
"liveWeb": "https://www.zdf.de/live-tv",
"liveApp": null,
"defaultOrder": 1000,
"hdp": false,
"quality": 2,
"name": "ZDFneo HD",
"isEncrypted": false,
"isHD": false,
"dvbTriplet": "dvb://0.0.0",
"id": null,
"major": true
}
this is connected to the shows through its groupID which shows up as channelID in the shows Object. Below is a sample for the shows object
{
"_id": "5b1f5c7da6cdf0cbbdb7e700",
"showID": 892149863,
"channelID": 16481,
"title": "Masters of Sex",
"subtitle": "Auf frischer Tat ertappt (Dirty Jobs)",
"serie": {
"no": "4",
"title": "Auf frischer Tat ertappt",
"seasonno": "2",
"info": "Staffel 2 | Folge 4"
}
this what I have done to query the data for channels
import stations from "../data/channels.json";
import data1 from "../data/1.json";
import data2 from "../data/2.json";
import data3 from "../data/3.json";
import data4 from "../data/4.json";
import data5 from "../data/5.json";
import data6 from "../data/6.json";
class Contents extends React.Component {
constructor(){
super();
this.trans = this.trans.bind(this);
}
station = { ...stations };
shows = { ...data1, ...data2, ...data3, ...data4, ...data5, ...data6 };
trans(){
Object.values(station.result.channels).map(value => {
console.log(value["groupID"], "odgdggddgdd");
return value["groupID"];
});
}
render() {
return (
<Fragment>
<TopNavBar />
<BottomNavBar />
<div className="row">
<section className="left-menus">
<div className="left-items">
{Object.values(station.result.channels).map(value => (
<div>
<img
src={`https://cdn.hd-plus.de/senderlogos/bright-cropped/${value["groupID"]}.png`}
alt=""
/>
</div>
))}
</div>
</section>
<section className="item-center">
{
Object.values(shows.result).map(value => (
<div className="shows">{
<div className="grid-items">
<div className="item">
<small>{value.startime}</small>
<small>value.resolution</small>
</div>
<div className="item-name">{value.title}</div>
</div>
}
</div>))}
</section>
</div>
</Fragment>
);
}
}
export default Contents;
I need some help with aligning the channels with their respective stations. I hope this is descriptive enough. Thank you
Updated Code for the tiles
<section className="item-center">
{
Object.values(station.result.channels).map(value => (
<div className="shows">{
shows.result.find(show => show['channelID'] === value['groupID']).map(item => (
<div className="grid-items">
<div className="item">
<small>{item.startime}</small>
<small>value.resolution</small>
</div>
<div className="item-name">{item.title}</div>
</div>
))}
</div>))}
</section>
error message
when I try to add Object.values() around it I get this
The correct solution for this (as found in the comments) is to use the filter() function. a find() function would only give back one object, or undefined, so you cannot use map on it.
shows.result.filter(show => show['channelID'] === value['groupID']).map(item =>
())
This will return every object where the channelID equals the groupID, which you can then map to a ui element.
https://www.w3schools.com/jsref/jsref_filter.asp

how can we present the previously entered search criteria in react-awesome-query-builder?

I am using react-awesome-query-builder to build query and search. I want to present the previously entered query in the same search when I log in again to the application. I meant some default selected criteria. I googled for a way to implement but unfortunately i would not get any idea.
Below is code
class QueryProcessor extends Component{
constructor(props){
super(props);
this.state={
query:null,
}
this.getChildren=this.getChildren.bind(this);
let query=null;
let mainDate=null;
}
getChildren(props) {
this.query=QbUtils.queryString(props.tree, props.config);
return (
<div>
<div className="query-builder">
<Builder {...props} />
</div>
</div>
)
}
onChange(tree) {
//here you can save tree object:
// var treeJSON = transit.toJSON(tree);
console.log("tree",tree);
}
render(){
let mainData=this.props.rulingList?this.props.rulingList:null;
console.log("mainData",mainData);
return(
<div className="page-content container-fluid header-master">
<div className="content content-background">
<div className="col-md-12 custom-back-white header-master">
<br/>
<div className="">
<h1> Query Builder</h1><br/>
<Query
{...config}
//you can pass object here, see treeJSON at onChange
// value=transit.fromJSON(treeJSON)
get_children={this.getChildren}
onChange={this.onChange}
></Query><br/>
<div className="pull-right">
<button type="button" className="btn btn-success" onClick={()=>{this.props.ListByQuery(this.query)}} >Search</button>
</div>
<br/>
</div>
</div>
</div>
</div>
);
}
}
Can you help me out to implement. Thanks in Advance
Well, in the demo here two ways are presented for serialization and loading tree
https://github.com/ukrbublik/react-awesome-query-builder/blob/master/examples/demo/demo.js#L18
I've tried the second way using transit-immutable-js
serializeTree = transit.toJSON;
loadTree = transit.fromJSON;
Here's a Formik field component that you can use like this
<Field
name="condition"
component={QueryBuilder}
fields={fields}
label="Conditions"
/>
The component:
/* eslint-disable jsx-a11y/label-has-for */
import React from 'react';
import PropTypes from 'prop-types';
import get from 'lodash.get';
import { Query, Builder, Utils as QbUtils } from 'react-awesome-query-builder';
import transit from 'transit-immutable-js';
import 'react-awesome-query-builder/css/styles.scss';
import 'react-awesome-query-builder/css/compact_styles.scss';
import { Form } from 'react-bootstrap';
import config from './config';
const QueryBuilder = ({
field,
form: { touched, errors, setFieldValue },
type,
id,
label,
className,
fields,
...props
}) => {
const getChildren = queryProps => {
return (
<div className="query-builder">
<Builder {...queryProps} />
</div>
);
};
return (
<Form.Group className={className} controlId={field.name}>
{label && <Form.Label>{label}</Form.Label>}
<Form.Control
as={Query}
get_children={getChildren}
isInvalid={get(touched, field.name) && get(errors, field.name)}
onChange={tree => {
setFieldValue(field.name, {
tree: transit.toJSON(tree),
mongo: JSON.stringify(
QbUtils.mongodbFormat(tree, { ...config, fields })
)
});
}}
fields={fields}
{...config}
{...props}
value={field.value ? transit.fromJSON(field.value.tree) : null}
/>
<Form.Control.Feedback type="invalid">
{get(errors, field.name)}
</Form.Control.Feedback>
</Form.Group>
);
};
QueryBuilder.propTypes = {
field: PropTypes.object.isRequired,
form: PropTypes.object.isRequired,
fields: PropTypes.object.isRequired,
type: PropTypes.string,
className: PropTypes.string,
id: PropTypes.string.isRequired,
error: PropTypes.string,
label: PropTypes.string,
onChange: PropTypes.func.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};
QueryBuilder.defaultProps = {
type: 'input',
className: '',
value: null,
label: null,
error: null
};
export default QueryBuilder;
Example config file : https://github.com/ukrbublik/react-awesome-query-builder#config-format
I don't recomment using transit anymore.
In latest versions I've added utils to save tree to json and load from json:
loadTree
getTree
See new examples

importing image dynamically (React Js) (Require img path cannot find module)

I need to import images(several) from my image file dynamically by a map method. First, I want to set a base URL to my images file and then read the image's name from my JSON file which includes the image property and then set the image src accordingly.
The JSON file is like below :
{
"title": "Blue Stripe Stoneware Plate",
"brand": "Kiriko",
"price": 40,
"description": "Lorem ipsum dolor sit amet...",
"image": "blue-stripe-stoneware-plate.jpg"
},
{
"title": "Hand Painted Blue Flat Dish",
"brand": "Kiriko",
"price": 28,
"description": "Lorem ipsum dolor sit amet...",
"image": "hand-painted-blue-flat-dish.jpg"
},
my images folder :
I have read the products by redux which is works perfectly =>
const products = this.props.products;
console.log(products, 'from redux');
const fetchProducts = [];
for (let key in products) {
fetchProducts.push({
...products[key]
});
}
the console.log() =>
Now I want to define a base URL like this which later use as image src by adding the image's name from the JSON file in the map method :
const baseUrl = '../../components/assets/images/';
const fetchProducts = [];
for (let key in products) {
fetchProducts.push({
...products[key]
});
}
const productCategory = fetchProducts.map((product, index) => {
return (
<Photo
key={index}
title={product.title}
brand={product.brand}
description={product.description}
imageSource={baseUrl + product.image}
imageAlt={product.title}
/>
);
});
my Photo component looks like below :
const photo = props => (
<div className={classes.Column}>
<img src={require( `${ props.imageSource }` )} alt={props.imageAlt} />
<div className={classes.Container}>
<p>{props.brand}</p>
<p>{props.title}</p>
<p>{props.price}</p>
</div>
</div>
);
export default photo;
unfortunately, I have faced this error:
Thanks in advance and sorry for my bad English :)
Import is not working like that. You can use a base URL like that:
const baseUrl = "../../components/assets/images/";
Then you can pass to your Photo component like that:
<Photo
key={index} // Don't use index as a key! Find some unique value.
title={product.title}
brand={product.brand}
description={product.description}
imageSource={baseUrl + product.image}
imageAlt={pro.title}
/>;
Lastly, in your Photo component use require:
<img src={require( `${ props.imageSource }` )} alt={props.imageAlt} />
or like that:
<img src={require( "" + props.src )} alt={props.imageAlt} />
But, don't skip "" part or don't use it directly like:
<img width="100" alt="foo" src={require( props.src )} />
since require wants an absolute path string and first two do this trick.
Try this solution for dynamic image:-
**Componet**
const photo = props => (
<div className={classes.Column}>
<img src={require( `../../components/assets/images/${props.imageSource}`)} alt={props.imageAlt} />
<div className={classes.Container}>
<p>{props.brand}</p>
<p>{props.title}</p>
<p>{props.price}</p>
</div>
</div>
);
**Include Componet**
const productCategory = fetchProducts.map((product, index) => {
return (
<Photo
key={index}
title={product.title}
brand={product.brand}
description={product.description}
imageSource={product.image}
imageAlt={product.title}
/>
);
});
So here is what I found and it worked for me.
"file-loader": "4.3.0"
React: 16.12
Run this in your terminal:
npm run eject
Check file-loader in config/webpack.config and located file-loader configurations. What I did was, I created a directory called static/media/{your_image_name.ext} following the notation there:
options: {
name: "static/media/[name].[hash:8].[ext]"
}
and then imported this image like
import InstanceName from "static/media/my_logo.png";
Happy Hacking!
After trying all kinds of solutions, including backticks <img src={require( `${ props.imageSource }` )} and others, nothing was working. I kept getting the error Cannot find module, even though my relative paths were all correct. The only primitive solution I found, if you have a relatively small number of possible images, is to predefine a Map that will map to the actual imports. (Of course this won't work for lots of images.)
import laptopHouse from '../../images/icons/laptop-house.svg'
import contractSearch from '../../images/icons/file-contract-search.svg'
..
const [iconMap, setIconMap] = useState({
'laptopHouse': laptopHouse,
'contractSearch': contractSearch,
..
});
..
<img src={iconMap[props.icon]} />
i solved this typescript issue as follows in my project.
hope this is helpful
export const countryData = {
'sl': { name: 'Sri Lanka', flag: '/flag-of-Sri-Lanka.png' },
'uk': { name: 'UK', flag: '/flag-of-United-Kingdom.png' },
'usa': { name: 'USA', flag: '/flag-of-United-States-of-America.png' },
'ca': { name: 'Canada', flag: '/flag-of-Canada.png' },
'It': { name: 'Italy', flag: '/flag-of-Italy.png' },
'aus': { name: 'Australia', flag: '/flag-of-Australia.png' },
'me': { name: 'Middle East', flag: '/flag-of-Middle-East.png' },
'other': { name: 'Other', flag: '/flag-of-World.png' }, };
"part of URL within Double quotes" + dynamic_URL_via_var combo worked for me.
<Avatar src={require('../assets'+countryData['uk']['flag'])} />
//json data.
"ProductSlider":[
{
"image":"Product1.jpg"
},
{
"image":"Product2.jpg"
}]
//mapping data list in parent component.
{sliderData.map((data) => (
<SwiperSlide className='w-10 '>
<ProductCard data={{imgSrc: data.image}} />
</SwiperSlide>
))}
//child component(linked image).
import React from 'react'
import { CardImg } from 'react-bootstrap'
export default function ProductCard(props) {
let {imgSrc} = props.data;
return (
<div className="overflow-hidden">
<div className='overflow-hidden bg-grey opacity-card' >
<CardImg
variant='top'
src={require(`../assets/images/${imgSrc}`)}
/>
</div>
<div className='text-center p-1 opacity-card-title' >
<div>Add to cart</div>
</div>
</div>
)
}
Even I got the same error
{gallery.map((ch, index) => {....
cannot find module '/../..................png'
I went wrong here, I used src instead of ch
Error
<Image src={src.image} />
Solved
<Image src={ch.image} />

Move method from one component to another in React and still be able to use in original

I am working to create a small contact app using React with ES6. I had some data displaying in the render function of a component - see the link to the question below for the original structure...
How to specify a key for React children when mapping over an array
However, because I was also putting a form on the same page and I needed to update my data in state, I had to move the data to a higher level component.
Now I'm having trouble traversing the components so that my original list of contacts shows up on the left. I had to remove most of what was in my render function on the contact-list component because it was completely breaking the build.
First, here is the address-book component with the form - this is working, both pulling in my initial 3 contacts from state, then concating new contacts from the form to the array. (Still need cleanup code here to make UI work right...)
import React from 'react';
import ContactList from './contact-list.jsx';
import ContactForm from './contact-form.jsx';
import ShortContact from './short-contact.jsx';
class AddressBook extends React.Component {
constructor() {
"use strict";
super();
this.state = {
showContacts: true,
contacts: [
{
id: 1,
fName: "aaa",
lName: "aaaaa",
imgUrl: "http://brainstorminonline.com/wp-content/uploads/2011/12/blah.jpg",
email: "aaa#aaaa.com",
phone: "999999999999"
},
{
id: 2,
fName: "bbbbb",
lName: "bbbbbbb",
imgUrl: "https://media.licdn.com/mpr/mpr/shrinknp_200_200/bbb.jpg",
email: "bbb#bbb-bbb.com",
phone: "888888888888"
},
{
id: 3,
fName: "Number",
lName: "Three",
imgUrl: "http://3.bp.blogspot.com/-iYgp2G1mD4o/TssPyGjJ4bI/AAAAAAAAGl0/UoweTTF1-3U/s1600/Number+3+Coloring+Pages+14.gif",
email: "three#ccccc.com",
phone: "333-333-3333"
}
];
};
}
render() {
"use strict";
return (
<div className="row address-book">
<div className="col-md-6">
<ContactList />
</div>
<div className="col-md-6">
<button className='btn' id="contact-submit-button" type="submit" value="Create Contact">Create New Contact </button>
<div>
<ContactForm addContact={this._addContact.bind(this)}/>
</div>
</div>
</div>
);
}
_addContact (fName, lName, company, email, phone, imgURL) {
"use strict";
const contact = {
id: this.state.contacts.length + 1,
fName,
lName,
company,
email,
phone,
imgURL
};
this.setState({ contacts: this.state.contacts.concat([contact]) });
}
_getContacts() {
"use strict";
return contactList.map((contact) => {
"use strict";
return (
<ShortContact contact={contact} key={contact.id}/>)
});
}
_getContactsTitle (contactCount) {
"use strict";
if(contactCount === 0) {
return 'No Contacts';
} else if (contactCount === 1) {
return '1 contact';
} else {
return `${contactCount} contacts`;
}
}
}
export default AddressBook;
However, the bottom 2 methods _getContacts and _getContactsTitle are the ones that are needed in my ContactForm component - which is this one:
import React from 'react';
import ShortContact from './short-contact.jsx';
class ContactList extends React.Component {
render() {
const contacts = this._getContacts();
return (
<div>
<h3>List of Contacts</h3>
<h4 className="contact-count">{this._getContactsTitle((contacts.length))}</h4>
<ul className="contact-list">
{contacts}
</ul>
</div>
);
}
}
export default ContactList;
The const that defines contacts as well as the <h4> through the <ul> is what breaks the app because as you can see it references the _getContactTitle method from the other component as well as {contacts} which is in the _getContacts method.
I'm guessing I need to do something like wrap them into functions and pass them - but I've gotten turned around and can't quite see how that would work here with React. Any help would be welcome. Thanks!
You need to pass the contacts down via props instead of trying to build the list of contacts in the parent to pass down. what I mean is this.
ADDRESS BOOK
render() {
"use strict";
let contacts = this._getContacts();
-----------^---------------^----------- get the list in the parent
return (
<div className="row address-book">
<div className="col-md-6">
<ContactList contacts={contacts} />
-------------------------------^-----------^---- important part here
</div>
<div className="col-md-6">
<button className='btn' id="contact-submit-button" type="submit" value="Create Contact">Create New Contact </button>
<div>
<ContactForm addContact={this._addContact.bind(this)}/>
</div>
</div>
</div>
);
}
then in your CONTACT LIST component just use the list as props.
render() {
let {contacts} = this.props;
------------^--------------^---- its in the props now
let title = contacts.length > 1 ? `${contacts.length} contacts` : contacts.length === 1 ? '1 contact' : 'No Contacts';
return (
<div>
<h3>List of Contacts</h3>
<h4 className="contact-count">{title}</h4>
<ul className="contact-list">
{contacts}
</ul>
</div>
);
}
}
from here you can remove the contactTitle function. let the child render that since it knows the length of the array (because its in props).
As a side note, in your _addContact function. instead of creating a new array and concatenating it with the state contact array just push the new one onto the current state. its better storage (i.e. dont need to make a new array to combine a new item to the array).
this.setState({ contacts: this.state.contacts.push(contact) });

Categories