Using react to fetch one item from json - json

So I got this problem.
I am trying to fetch a local json file from my project. The json file is stored in the src folder.
This is my json file
[
{
"name": "Sunsssset Beach",
"email": "info#sunsetbeach.com",
"image": "https://images.unsplash.com/photo-1439130490301-25e322d88054?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1489&q=80",
"price": 85,
"maxGuests": 18,
"lat": 60.393388,
"lng": 5.22872,
"description": "Get ready for some amazing sunsets as you sip a cocktail and watch dolphins play in the harbour below.",
"selfCatering": true,
"createdAt": "2020-09-04T09:07:10.367Z",
"id": "5f5203bedc17b0a4b302f211"
},
{
"name": "The Hideaway",
"email": "info#hideaway.com",
"image": "https://images.unsplash.com/photo-1551906993-c8b38a6ab201?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=666&q=80",
"price": 70,
"maxGuests": 2,
"lat": 60.425168,
"lng": 5.358141,
"description": "This secluded wilderness cabin is the perfect spot for a restful and cosy getaway.",
"selfCatering": true,
"createdAt": "2020-09-04T09:07:10.370Z",
"id": "5f5203bedc17b0a4b302f213"
},
{
"name": "Rest Easy",
"email": "management#resteasy.com",
"image": "https://images.unsplash.com/photo-1512552288940-3a300922a275?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=751&q=80",
"price": 120,
"maxGuests": 14,
"lat": 60.396779,
"lng": 5.235602,
"description": "Need some time off from your busy life to relax and unwind? Choose Rest Easy for the complete relaxation experience you desire.",
"selfCatering": false,
"createdAt": "2020-09-04T09:07:10.370Z",
"id": "5f5203bedc17b0a4b302f212"
}
]
And here is my project file
function Establishment() {
const [establishment, setEstablishment] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("jsonfile")
.then((response) => {
// check if response returns ok
if (response.ok) {
return response.json();
} else {
setErrorHandle(true);
}
})
.then((data) => {
setEstablishment(data);
})
.catch((err) => {
console.log(err);
setErrorHandle(true);
})
.finally(() => setLoading(false));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Now I want to be able to press a button do display all of these items, and by clicking on one item, it should display only that item in a different page. How do I do that?
Let me know if there is anything else you need to solve this problem :)

Firstly if the JSON data is in a local in your file system then you do not need fetch or axios
Why you did not use Axios or Fetch?
Axios or Fetch use to get or post data into the server. In our case, we read on the local folder file so that we use map().
Note: most important when using Axios and Fetch we need URL parameter compulsory
Now what you are to do is:
1. Firstly Import the file wherever you want to use:
import data from "../static/data/myData.json";
2. Now you want to map through that Json file:
<div>
{
data.map(myData, index)=>{
return <div key={myData.id}>
<h1>{myData.name}</h1>
<h3>{myData.email}</h3>
</div>
}
}
</div>
So that is how you want to handle this situation obviously structuring the content the way you desire not with h1's and h3's like I did

Related

How is the ipcRenderer argument JSON object losing data when passed from an Angular 7 service?

I am having a strange issue with an Angular 7.1.1 and Electron 4.1.4 project.
Data Flow:
Angular Component "Report Builder" collects report configuration options from a FormGroup and FormControl validated form and sends data to docx-templater.service
User Button triggers createReport() function
When submitting options for a complete report, the createReport() function calls dataService's fnGetCompleteControlList() which returns properly configured JSON asynchronously.
with a .then() function after the async data retrieval, the createReport() function combines the output directory which is part of the configuration form and sends both to the docx-templater.service's createCompleteDocument() function. Once the promise is returned it updates the UI.
Angular Service "docx-templater"'s createCompleteDocument function passes the data and folder values to the ipcRenderer.send for the electron "writeCompleteDocument" channel and returns a promise.
In my main.ts, I have an ipcMain.on for the "writeCompleteDocument" channel that passes the data to a write-docx function for processing that data into a word document.
Problem:
When the data gets to my write-docx function it is missing a sub array of objects that are essential to the export process.
I have verified that the data is perfect in the Chrome Developer Tools console of electron at the moment just before it sends the data to the docx-templater.service and just before that service sends it to the ipcRenderer (meaning my data service and Report Builder functions are working as designed). When I check the data in the main.ts by saving the data off to a JSON file it is missing the controls sub array within the second object of the JSON only. The controls sub array shows up in the first object as expected.
I will note that what is coming out of the ipcMain function is a properly formed JSON file so it has really just excluded the "controls" sub array and is not truncating due to memory or buffer limits or anything like that.
report-builder.component.ts
createReport() {
if (this.reportBuilderFG.get('allControls').value) {
this.db.fnGetCompleteControlList()
.then((groups: Group[]) => {
this.word.createCompleteDocument(groups, this.reportBuilderFG.get('folder').value + '\\filename.docx')
.then(() => {
this.openSnackBar(this.reportBuilderFG.get('folder').value + '\\filename.docx created successfully');
});
});
} else {
// Do other stuff
}
docx-templater.service.ts
createCompleteDocument(data, folder: string): Promise<boolean> {
return new Promise(resolve => {
console.log(data) <=== Data is perfect here.
ipcRenderer.send('writeCompleteDocument', {data: data, folder: folder});
resolve();
});
}
main.ts
import { writeCompleteDocument } from './node_scripts/write-docx';
ipcMain.on('writeCompleteDocument', (event, arg) => {
fs.writeFileSync("IPCdata.json", arg.data); // <==== Part of the data is missing here.
writeCompleteDocument(arg.data, arg.folder);
});
Good Data Example (some keys and objects excluded for brevity)
[
{
"name": "General Security",
"order": 1,
"subgroups": [
{
"_id": "GOV",
"name": "Governance",
"order": 1,
"controls": [
{
"group": "GS",
"subgroup": "GOV",
"active": true,
"printOrder": 1,
"name": "This is my GS control name",
"requirements": [
{
"id": "SA01",
"active": true,
"order": 1,
"type": "SA",
"applicability": [
"ABC",
"DEF",
"GHI"
],
},
{ ... 3 more }
],
"_id": "GSRA-03",
"_rev": "1-0cbdefc93e56683bc98bae3a122f9783"
},
{ ... 3 more }
],
"_id": "GS",
"_rev": "1-b94d1651589eefd5ef0a52360dac6f9d"
},
{
"order": 2,
"name": "IT Security",
"subgroups": [
{
"_id": "PLCY",
"order": 1,
"name": "Policies",
"controls": [ <==== This entire sub array is missing when exporting from IPC Main
{
"group": "IT",
"subgroup": "PLCY",
"active": true,
"printOrder": 1,
"name": "This is my IT control name",
"requirements": [
{
"id": "SA01",
"active": true,
"order": 1,
"type": "SA",
"applicability": [
"ABC",
"DEF",
"GHI"
],
}
],
"_id": "GSRA-03",
"_rev": "1-0cbdefc93e56683bc98bae3a122f9783"
}
}
],
"_id": "IT",
"_rev": "2-e6ff53456e85b45d9bafd791652a945c"
}
]
I would have expected the ipcRenderer to pass a JSON exactly as it is to the ipcMain.on function, but somehow it is trimming part of the data. I have even tried strigifying the data before sending it to the renderer and then parsing it on the other side but that did nothing.
Could this be an async thing? I am at a loss of where to go next to debug and find what idiot mistake I made in the process.
Also, I realize that the above data flow seems overly complex for what I am doing, and that I can probably do it easier, but it makes sense (kinda) for the way the whole application is structured so I am going to go with it if I can squash this bug.
Looks like your createCompleteDocument() function is set up incorrectly. A quick search showed me that ipcRenderer is an async function, but you are responding to it (almost) synchronously.
You have the following, which is (probably) incorrect (actually it's definitely incorrect, because you've typed typed the return as Promise<boolean> when it is Promise<void>):
createCompleteDocument(data, folder: string): Promise<boolean> {
return new Promise(resolve => {
ipcRenderer.send('writeCompleteDocument', {data: data, folder: folder});
resolve();
});
}
ipcRenderer#send() is async but you are calling resolve() immediately afterwards without waiting for the function to resolve. This probably explains why adding the setTimeout() is fixing the problem for you. Looking at the ipcRenderer docs, the following probably does what you want:
createCompleteDocument(data, folder: string): Promise<Event> {
return new Promise(resolve => {
ipcRenderer.once('writeCompleteDocument', resolve);
ipcRenderer.send('writeCompleteDocument', {data: data, folder: folder});
});
}
Looks like the callback is passed an Event object.
Another option would be to simply replace ipcRenderer#send() with ipcRenderer#sendSync() in your original code, but as pointed out in that method's documentation :
Sending a synchronous message will block the whole renderer process, unless you know what you are doing you should never use it.
Making use of ipcRenderer#send() and ipcRenderer#once() is almost definitely the way to go.
Seperately, you can clean up the code by switching to async/await functions. For example:
async createReport(): Promise<void> {
if (this.reportBuilderFG.get('allControls').value) {
const groups: Group[] = await this.db.fnGetCompleteControlList();
await this.word.createCompleteDocument(
groups,
this.reportBuilderFG.get('folder').value + '\\filename.docx'
);
// Unclear if this function is actually async
await this.openSnackBar(
this.reportBuilderFG.get('folder').value +
'\\filename.docx created successfully'
);
} else {
// Do other stuff
}
}
I was able to solve this by adding a 1000 ms timeout after my fnGetCompleteControlList() data pull in the report-builder.component.ts. It seems like I have a lot more work todo with learning async functions. :-(
report-builder.component.ts
createReport() {
if (this.reportBuilderFG.get('allControls').value) {
this.db.fnGetCompleteControlList()
.then((groups: Group[]) => {
setTimeout(() => {
this.word.createCompleteDocument(groups, this.reportBuilderFG.get('folder').value + '\\filename.docx')
.then(() => {
this.openSnackBar(this.reportBuilderFG.get('folder').value + '\\filename.docx created successfully');
});
}, 1000);
});
} else {
// Do other stuff
}

Custom routes in json-server using Node.js

I found problem when using custom routes for requesting db.json which is deployed in json-server.
For example for this given json as below, I'd like to access voters by name, so when I type this
url:
https://localhost:3000/data/1/sessions/:sessionId/voters/:voterName
Where are sessionId and voterName are parameterized, the response should be {"voterName"}
db.json:
{
"id": 1,
"name": "Angular Connect",
"date": "2036-09-25T23:00:00.000Z",
"time": "10:00 am",
"price": 599.99,
"imageUrl": "/assets/images/angularconnect-shield.png",
"location": {
"address": "1057 DT",
"city": "London",
"country": "England"
},
"sessions": [
{
"id": 1,
"name": "Using Angular 4 Pipes",
"presenter": "Peter Bacon Darwin",
"duration": 1,
"level": "Intermediate",
"abstract": "Learn all about the new pipes in Angular 4, both \n how to write them, and how to get the new AI CLI to write \n them for you. Given by the famous PBD, president of Angular \n University (formerly Oxford University)",
"voters": [
"bradgreen",
"igorminar",
"martinfowler"
]
},
{
"id": 2,
"name": "Getting the most out of your dev team",
"presenter": "Jeff Cross",
"duration": 1,
"level": "Intermediate",
"abstract": "We all know that our dev teams work hard, but with \n the right management they can be even more productive, without \n overworking them. In this session I'll show you how to get the \n best results from the talent you already have on staff.",
"voters": [
"johnpapa",
"bradgreen",
"igorminar",
"martinfowler"
]
},
{
"id": 3,
"name": "Angular 4 Performance Metrics",
"presenter": "Rob Wormald",
"duration": 2,
"level": "Advanced",
"abstract": "Angular 4 Performance is hot. In this session, we'll see \n how Angular gets such great performance by preloading data on \n your users devices before they even hit your site using the \n new predictive algorithms and thought reading software \n built into Angular 4.",
"voters": []
},
{
"id": 4,
"name": "Angular 5 Look Ahead",
"presenter": "Brad Green",
"duration": 2,
"level": "Advanced",
"abstract": "Even though Angular 5 is still 6 years away, we all want \n to know all about it so that we can spend endless hours in meetings \n debating if we should use Angular 4 or not. This talk will look at \n Angular 6 even though no code has yet been written for it. We'll \n look at what it might do, and how to convince your manager to \n hold off on any new apps until it's released",
"voters": []
},
{
"id": 5,
"name": "Basics of Angular 4",
"presenter": "John Papa",
"duration": 2,
"level": "Beginner",
"abstract": "It's time to learn the basics of Angular 4. This talk \n will give you everything you need to know about Angular 4 to \n get started with it today and be building UI's for your self \n driving cars and butler-bots in no time.",
"voters": [
"bradgreen",
"igorminar"
]
}
]
}
For showing the last result I used custom routes in NodeJS like below:
router.js:
var jsonServer = require('json-server')
const low = require('lowdb')
var server = jsonServer.create()
const db = low('db.json')
// Add custom routes before JSON Server router
server.get('/data/:id/sessions/:sessionId/voters/:voterName', function (req, res) {
// See https://github.com/typicode/lowdb
var user=db.get("sessions")
.find({id:sessionId})
.get("voters")
.find({voterName})
.value()
if (user) {
res.jsonp(user)
} else {
res.sendStatus(404)
}
})
server.use(function (req, res, next) {
if (req.method === 'POST') {
req.body.createdAt = Date.now()
}
// Continue to JSON Server router
next()
})
// Use default router
// server.use(router)
server.listen(3000, function () {
console.log('JSON Server is running')
})
But when I run this command:
json-server --watch db.json --middlewares router.js
I get the following error:
TypeError: app.use() requires a middleware function
at Function.use (C:\Users\maoutir\AppData\Roaming\npm\node_modules\json-server\node_modules\express\lib\application.js:210:11)
at createApp (C:\Users\maoutir\AppData\Roaming\npm\node_modules\json-server\lib\cli\run.js:79:9)
at load (C:\Users\maoutir\AppData\Roaming\npm\node_modules\json-server\lib\cli\run.js:148:13)
at module.exports (C:\Users\maoutir\AppData\Roaming\npm\node_modules\json-server\lib\cli\utils\load.js:37:5)
at start (C:\Users\maoutir\AppData\Roaming\npm\node_modules\json-server\lib\cli\run.js:125:5)
at module.exports (C:\Users\maoutir\AppData\Roaming\npm\node_modules\json-server\lib\cli\run.js:162:3)
at module.exports (C:\Users\maoutir\AppData\Roaming\npm\node_modules\json-server\lib\cli\index.js:81:3)
at Object.<anonymous> (C:\Users\maoutir\AppData\Roaming\npm\node_modules\json-server\lib\cli\bin.js:3:14)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
Thanks in advance for your answer.
I think you should not use the --middlewares options to start your server, router.js is not a middleware. so here it try to add your router.js as a middleware and fail to start
could you try :
json-server router.js --watch db.json
There are two options to use json server one is running it through your own js file the other is through running the middle wares you are running the js file you should be running middlewares as such https://github.com/typicode/json-server#add-middlewares only create the module.export as shown in the link. You don't need all the fancy server stuff.

Import JSON file in React with data format error

I tried to import a JSON file in my React project, but got the parsing error:
json file:testData.json
{
"data": {
"articles": [
{
"id": "95c12a8f6c88953ca8f8a39da25546e6",
"title": "Introducing React's Error Code System",
"date": "Mon Jul 11 2016 00:00:00 GMT+0000 (UTC)",
"authorId": "2c6aa2cfe3449467d329fa17d6ea230f",
"body": "Building a better developer experience has been one of the things that React deeply cares about, and a crucial part of it is to detect anti-patterns/potential errors early and provide helpful error messages when things (may) go wrong. However, most of these only exist in development mode; in production, we avoid having extra expensive assertions and sending down full error messages in order to reduce the number of bytes sent over the wire."
}
],
"authors": [
{
"id": "d85577ea34ae50f2dac5347b5219aa23",
"firstName": "Andrew",
"lastName": "Clark",
"website": "https://twitter.com/acdlite"
}
]
}
}
DataApi.js file:
export default class DataApi {
// property: rawData
constructor(rawData) {
this.rawData = rawData;
}
mapIntoObject(arr) {
return arr.reduce((acc, curr) => {
acc[curr.id] = curr;
return acc;
}, {});
}
getArticle() {
return this.mapIntoObject(this.rawData.articles);
}
getAuthors() {
return this.mapIntoObject(this.rawData.authors);
}
}
And I tried to import JSON data in this file:
import DataApi from "./DataApi"; // object to process data
import { data } from "./testData.json"; // raw data
// create a api object to host raw data
let api = new DataApi(data);
const articles = api.getArticle();
console.log(articles);
then I got the error:(the import directory are correct):
2:13 error Parsing error: Unexpected token, expected ";"
1 | {
> 2 | "articles": [
| ^
3 | {
4 | "id": "95c12a8f6c88953ca8f8a39da25546e6",
5 | "title": "Introducing React's Error Code System",
What is the problem?
You can do export default
testData.json:
const data = {
"data": {
"articles": [
{
"id": "95c12a8f6c88953ca8f8a39da25546e6",
"title": "Introducing React's Error Code System",
"date": "Mon Jul 11 2016 00:00:00 GMT+0000 (UTC)",
"authorId": "2c6aa2cfe3449467d329fa17d6ea230f",
"body": "Building a better developer experience has been one of the things that React deeply cares about, and a crucial part of it is to detect anti-patterns/potential errors early and provide helpful error messages when things (may) go wrong. However, most of these only exist in development mode; in production, we avoid having extra expensive assertions and sending down full error messages in order to reduce the number of bytes sent over the wire."
}
],
"authors": [
{
"id": "d85577ea34ae50f2dac5347b5219aa23",
"firstName": "Andrew",
"lastName": "Clark",
"website": "https://twitter.com/acdlite"
}
]
}
}
export default data;
and while importing
With json-loader installed, you can use
import data from "./testData.json";
or If you have used create-react-app to scaffold your project, the module is already included, you just need to import your json:
import data from "./testData";
To install json-loader
npm install --save-dev json-loader
And add below config to your webpack.config.js
webpack.config.js
module.exports = {
module: {
loaders: [
{
test: /\.json$/,
loader: 'json-loader'
}
]
}
}
You have to export your json data, your json data should be like this.
export const data = {
"data": {
"articles": [
{
"id": "95c12a8f6c88953ca8f8a39da25546e6",
"title": "Introducing React's Error Code System",
"date": "Mon Jul 11 2016 00:00:00 GMT+0000 (UTC)",
"authorId": "2c6aa2cfe3449467d329fa17d6ea230f",
"body": "Building a better developer experience has been one of the things that React deeply cares about, and a crucial part of it is to detect anti-patterns/potential errors early and provide helpful error messages when things (may) go wrong. However, most of these only exist in development mode; in production, we avoid having extra expensive assertions and sending down full error messages in order to reduce the number of bytes sent over the wire."
}
],
"authors": [
{
"id": "d85577ea34ae50f2dac5347b5219aa23",
"firstName": "Andrew",
"lastName": "Clark",
"website": "https://twitter.com/acdlite"
}
]
}
}
change .json to .js extention, while importing
import { data } from "./testData"; // raw data

React unable to rerender after setState using Fetch

I'm at my wits end. I'm a coding novice trying to use .map() to iterate through JSON data and display it in a card on React.
I fetch the data under componentDidMount() and use setState to assign it. This works completely fine on another page.
However, on this page I am trying to iterate through the 'projects' array on this object, but whenever I try to .map() into the products array I get errors.
Even with a simple console.log I get errors.
I think this has to do with asynchronous fetching but all the questions I see address this with setState(). I don't know what to do.
Here is my JSON object:
{
"_id": "59dac308b9267fbcb5d2de32",
"name": "The Jewelry Counter",
"description": "Limited edition fine jewelry, handcrafted in the USA",
"image": "/public/images/tjclogo.png",
"__v": 0,
"products": [
{
"_id": "59dada32b9267fbcb5d2de37",
"name": "Opal and cubic zirconia stacking ring set",
"price": 80,
"description": "A three ring stacking set that features an arced ring with an opal and cz, one with just glowing cz's and one with a single solitaire prong set opal.",
"img": "/shopping-cart-app/public/images/ringset.png",
"quantity": 2,
"__v": 0,
"keywords": [
"ring",
"opal",
"cubic zirconia",
"cz",
"jewelry",
"womens",
"jewelry counter"
]
},
{
"_id": "59dadae1b9267fbcb5d2de38",
"name": "Moonstone Ear Jackets",
"price": 140,
"description": "Four teardrop shaped glowing moonstones are prong set and attach to your ear in a simple three piece process that makes it look as though they are floating on your ears.",
"img": "/shopping-cart-app/public/images/moonearrings.png",
"quantity": 4,
"__v": 0,
"keywords": [
"earrings",
"moonstone",
"jewelry",
"womens",
"jewelry counter"
]
},
{
"_id": "59dadb79b9267fbcb5d2de39",
"name": "Horizontal Pyrite Necklace",
"price": 52,
"description": "A horizontal bar of hand crushed pyrite is attached to a brass bar on a 16\" brass chain.",
"img": "/shopping-cart-app/public/images/pyritenecklace.jpg",
"quantity": 1,
"__v": 0,
"keywords": [
"necklace",
"pyrite",
"jewelry",
"womens",
"jewelry counter"
]
},
{
"_id": "59dadcfbb9267fbcb5d2de3a",
"name": "Purple Tourmaline Promise Rings",
"price": 48,
"description": "Faceted purple tourmaline prong set on an 18K yellow gold vermeil band. This simple ring is perfect for stacking.",
"img": "/shopping-cart-app/public/images/ring.jpg",
"quantity": 1,
"__v": 0,
"keywords": [
"ring",
"tourmaline",
"jewelry",
"womens",
"jewelry counter"
]
}
]
}
I have the following code:
import React, { Component } from 'react';
import StoreCard from '../../StoreCard';
import ProductCard from '../../ProductCard';
import Wrapper from '../../Wrapper';
import Header from '../../Header';
import StoreLogin from "../../StoreLogin";
import Store from "../../Store";
import './Shop.css';
class Shop extends Component {
constructor(props) {
super(props);
this.state = { storeInfo: []};
}
// state = {storeInfo: [],
// products: []
// };
componentDidMount() {
fetch('/stores/59dac308b9267fbcb5d2de32')
.then(res => res.json())
.then((storeInfo) => {this.setState({ storeInfo: storeInfo })})
}
render() {
console.log(this.state.storeInfo) // works,displays the entire JSON object after beig called twice in the console.
console.log(this.state.storeInfo.name); // WORKS, same as above
console.log(this.state.storeInfo['products'][1]['name']); //DOES NOT WORK - ERRORS
// console.log(this.state.storeInfo.products[1].name); //DOES NOT WORK - ERRORS
return (
<div>
<Header location="Search all stores"/>
<Wrapper>
<StoreLogin
id={this.state.storeInfo._id} // works
userName={this.state.storeInfo.name} // works
// productName={this.state.storeInfo.products[1].name} //does not work
>
</StoreLogin>
</Wrapper>
<h1>Shop</h1>
</div>
);
}
}
export default Shop;
When I uncomment the 'console.logs' and the 'productName' in the storeLogin component one at a time, I get 3 errors:
Uncaught TypeError: Cannot read property '1' of undefined
Then
proxyConsole.js:54 The above error occurred in the <Shop> component:
in Shop (created by Route)
in Route (at App.js:22)
in div (at App.js:19)
in Router (created by BrowserRouter)
in BrowserRouter (at App.js:17)
in App (at index.js:6)
Consider adding an error boundary to your tree to customize error handling behavior.
You can learn more about error boundaries at https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html
And then
Uncaught TypeError: Cannot read property '1' of undefined
again.
The reason is data you are trying to access storeInfo['products'][1]['name'] is not yet available in the render function initially.So you need to check if data is present.For that what you can do is
render() {
console.log(this.state.storeInfo.name); // WORKS
//Move this console inside if
if ( this.state.storeInfo.products && this.state.storeInfo.products.length) {
console.log(this.state.storeInfo['products'][1]['name']);
console.log(this.state.storeInfo.products[0].name);
}
return (
<div>
<Header location="Search all stores"/>
<Wrapper>
<StoreLogin
id={this.state.storeInfo._id} // works
userName={this.state.storeInfo.name} // works
productName={this.state.storeInfo.products && this.state.storeInfo.products.length &&
this.state.storeInfo.products[0].name} //<-- Same here
>
</StoreLogin>
</Wrapper>
<h1>Shop</h1>
</div>
);
}
The problem is that, you are having a fetchRequest in the componentDidMount function and then setting the state, however your render is called before it and since you try to access this.state.storeInfo.products[0].name, this.state.storeInfo.products is undefined and hence you get an error, do a check before using it
return (
<div>
<Header location="Search all stores"/>
<Wrapper>
<StoreLogin
id={this.state.storeInfo._id} // works
userName={this.state.storeInfo.name} // works
productName={this.state.storeInfo.products && his.state.storeInfo.products[0]?
this.state.storeInfo.products[0].name: ''}
>

How to 'run' an IVR saved in JSON?

I am working on a IVR solution for small businesses in my local area but I am having trouble wrapping my head around how Node will handle menus. I could make a seperate Node server for each of my customers but I would like to have a single server that pulls each customer's IVR setup from a Mongo database or file when their number is called. I have an idea on how to save the menu structure in JSON but I am lost when it comes to turning that JSON into responses to <gather> inputs. I was thinking I could use a JSON structure like this in the DB (or maybe as a .json file on Amazon S3):
{
"menu": {
"id": 1,
"name": "Main",
"script": "Thank you for calling Local Company. To speak to sales press 1, ...",
"options": [
{
"name": "",
"action": "",
"value": "",
"next": ""
},
{
"name": "Sales",
"action": "dial",
"value": 12345678901,
"next": ""
},
{
"name": "Support",
"action": "dial",
"value": 12345678902,
"next": ""
},
{
"name": "Directions",
"action": "say",
"value": "Our offices are located at...",
"next": 1
},
{
"name": "Mailbox",
"action": "mailbox",
"value": "main",
"next": 1
}
]
}
}
Twilio developer evangelist here.
If you can return the JSON based on the number a user is dialling, then you could do something like this:
const Twilio = require('twilio');
app.post('/voice', (req, res) => {
const dialledNumber = req.body.To;
getIVRObjectFromPhoneNumber(dialledNumber, (IVRObject) => {
const twiml = Twilio.twiml.VoiceResponse();
if (typeof req.body.Digits !== 'undefined') {
// A user has pressed a digit, do the next thing!
const action = IVRObject.menu.options[req.body.Digits]
twiml[action.action](action.value);
} else {
// No digits yet, return the <Gather>
const gather = twiml.gather({
numDigits: 1
});
gather.say(IVRObject.script);
}
res.send(twiml.toString());
});
});
This doesn't quite use all of your object, I'm not sure what the values for next mean, but hopefully it's a start. The getIVRObjectFromPhoneNumber method is my made up, asynchronous method that returns a JavaScript object parsed from your example JSON above.
Let me know if this helps at all.