I'm new to NextJS, and trying to figure out, how to create a global variable that I could assign a different value anytime. Could someone give a simple example? (I know global might not be the best approach, but still I would like to know how to set up a global variable).
Let's say:
_app.js
NAME = "Ana" // GLOBAL VARIABLE
page_A.js
console.log(NAME) // "Ana"
NAME = "Ben"
page_B.js
console.log(NAME) // "Ben"
try using Environment Variables
/next.config.js
module.exports = {
env: {
customKey: 'my-value',
},
}
/pages/page_A.js
function Page() {
return <h1>The value of customKey is: {process.env.customKey}</h1>
}
export default Page
but you can not change its contents, except by changing it directly in next.config.js
Nextjs no special ways to provide global variables you want. You can achieve by:
Stateful management tool, like redux-react
Using Context
It's not like it's impossible,
I created a file called _customGlobals.jsx and put this as content
String.prototype.title = function () {
const sliced = this.slice(1);
return (
this.charAt(0).toUpperCase() +
(sliced.toUpperCase() === sliced ? sliced.toLowerCase() : sliced)
);
};
and imported it in _app.jsx like this:
import "./_customGlobals";
So now I can call this function on any string anywhere in my project like this:
"this is a title".title()
Database designed for this purpose. But for one var it's not wise to install whole db!
So, you can do it in a JSON file.
Add a var to a JSON file and use a function to update it.
this is a simple function for this usage:
const fs = require('fs');
function updateJSONFile(filePath, updates) {
fs.readFile(filePath, 'utf8', function (err, data) {
if (err) {
console.error(err);
return;
}
let json = JSON.parse(data);
for (let key in updates) {
json[key] = updates[key];
}
fs.writeFile(filePath, JSON.stringify(json, null, 2), 'utf8', function (err) {
if (err) {
console.error(err);
}
});
});
}
So, use it like this:
updateJSONFile('file.json', { name: 'John Doe', age: 30 });
You can create another function to read it dynamicly:
function readJSONFile(filePath) {
fs.readFile(filePath, 'utf8', function (err, data) {
if (err) {
return callback(err);
}
let json;
try {
json = JSON.parse(data);
} catch (error) {
return callback(error);
}
return callback(null, json);
});
}
and you can use it like this:
const readedFile = readJSONFile('file.json')
I deleted the callback function to have a simple code but you can add callback function to log error messages.
Related
I am trying to create a design for tags of entities in PouchDB with ReactJS. I managed to save my design using the put function, but when I query my design, the response is just an empty array and I am getting following error in console:
TypeError: Cannot read property 'emit' of undefined
I think the problem is in my function that I later use as a map parameter to my design variable:
function emitTagsMap(doc)
{
if (doc !== undefined)
{
if (Array.isArray(doc.tags))
{
doc.tags.forEach(x =>
{
/* Here is probably the problem - this.db is undefined */
this.db.emit(x, null);
});
}
}
};
this.db is declared in constructor:
constructor(service, name)
{
if (!service || !name) throw new Error("PouchDatabase initialized incorrectly");
this.name = name;
this.db = new PouchDB(name);
this.service = service;
this.tagsView();
}
Please bare in mind that I am completely new to PouchDB.
Any ideas how can I initialize the emit function?
Thank you in advance.
I assume, that your function is a part of a JavaScript class (otherwise you have to explain the idea with this). In ES6, you have to bind this to your regular functions. You have two options:
First - bind it via constructor:
constructor() {
this.emitTagsMap = this.emitTagsMap.bind(this);
}
Second - declare the function as an arrow one. This way, react will bind it for you:
emitTagsMap = (doc) =>
{
if (doc !== undefined)
{
if (Array.isArray(doc.tags))
{
doc.tags.forEach(x =>
{
/* Here is probably the problem - this.db is undefined */
this.db.emit(x, null);
});
}
}
};
You don't need to call emit over the database object.
Try this:
function emitTagsMap(doc)
{
if (doc !== undefined)
{
if (Array.isArray(doc.tags))
{
doc.tags.forEach(x =>
{
emit(x, null);
});
}
}
};
According to the PouchDB docs a design document is formed like this:
// first create a new design doc and pass your map function as string into it
var ddoc = {
_id: "_design/my_index",
views: {
by_name: {
map: "function (doc) { if (doc !== undefined) { if (Array.isArray(doc.tags)) { doc.tags.forEach(x => { emit(x, null); }); } } }"
}
}
};
// save it
db.put(ddoc).then(function () {
// success!
}).catch(function (err) {
// some error (maybe a 409, because it already exists?)
});
//Then you actually query it, by using the name you gave the design document when you saved it:
db.query('my_index/by_name').then(function (res) {
// got the query results
}).catch(function (err) {
// some error
});
https://pouchdb.com/guides/queries.html
I am trying to run some API calls in parallel, but am having problems since I am trying to call a function again before the API data has been returned.
I am thinking that I could possibly use the new command in Node, but am not sure how to structure it into this scheme. I am trying to avoid recursion, as I already have a recursive version working and it is slow.
Currently I am trying to this code on the server.
loopThroughArray(req, res) {
for(let i=0; i<req.map.length; i++) {
stack[i] = (callback) => {
let data = getApi(req, res, req.map[i], callback)
}
}
async.parallel(stack, (result) => {
res.json(result)
})
}
....
function getApi(req, res, num, cb) {
request({
url: 'https://example.com/api/' + num
},
(error, response, body) => {
if(error) {
// Log error
} else {
let i = {
name: JSON.parse(body)['name'],
age: '100'
}
console.log(body) // Returns empty value array.length > 1 (req.map[i])
cb(i)
}
})
Is there a way to spawn new instances of the function each time it's called and accumulate the results to send back as one result to the client?
Here's an example of calling Web APIs (each with different parameters), using the Async library, we start by creating an array of N function variables.
const async = require('async');
const request = require('request');
//Set whatever request options you like, see: https://github.com/request/request#requestoptions-callback
var requestArray = [
{url: 'https://httpbin.org/get'},
{url: 'https://httpbin.org/ip'}
];
let getApi = function (opt, callback) {
request(opt, (err, response, body) => {
callback(err, JSON.parse(body));
});
};
const functionArray = requestArray.map((opt) => {
return (callback) => getApi(opt, callback);
});
async.parallel(
functionArray, (err, results) => {
if (err) {
console.error('Error: ', err);
} else {
console.log('Results: ', results.length, results);
}
});
You can easily switch the Url and Query values to match whatever you need. I'm using HttpBin here, since it's good for illustrative purposes.
I am trying to fetch ordered data from Firebase and set it to state highscoreArray but it gives error "undefined is not a function (evaluating 'this.setState({ highscoreArray:sortedHighscores })')
componentDidMount() {
const reference = database.ref("highscores");
// Pushing sorted data to highscoreArray.
reference.orderByChild("highscore").limitToLast(3).on("value", function (snapshot) {
sortedHighscores = [];
snapshot.forEach(function (child) {
sortedHighscores.push({
"username": child.val().username,
"score": child.val().highscore
});
});
sortedHighscores = sortedHighscores.reverse();
console.log("sortedh", sortedHighscores); // fetch success
this.setState({highscoreArray: sortedHighscores}); // gives error
});
}
One of the major advantages of arrow functions is that it does not have it's own this value. It's this is lexically bound to the enclosing scope.
class Logger {
dumpData(data) {
var _this = this;
// this dumps data to a file and get the name of the file via a callback
dump(data, function (outputFile) {
_this.latestLog = outputFile;
});
}
}
// using arrow functions
class Logger {
dumpData(data) {
dump(data, outputFile => this.latestLog = outputFile);
}
}
1.this not accessible within loop so use variable let that = this the use that wherever you need this in this function.
componentDidMount() {
const reference = database.ref("highscores");
let that = this // here your variable declaration
// Pushing sorted data to highscoreArray.
reference.orderByChild("highscore").limitToLast(3).on("value", function (snapshot) {
sortedHighscores = [];
snapshot.forEach(function (child) {
sortedHighscores.push({
"username": child.val().username,
"score": child.val().highscore
});
});
sortedHighscores = sortedHighscores.reverse();
console.log("sortedh", sortedHighscores); // fetch success
that.setState({highscoreArray: sortedHighscores}); // gives error
});
}
Hope this will help you :) happy coding!
Inside the function callback the this has a different context. Either use an arrow function, or store a reference outside:
Arrow:
reference.orderByChild("highscore").limitToLast(3).on("value", (snapshot) => { ... });
I want to assign snap.val() to this.Productslike this.Products= snap.val(); but this.Products is undefined in that scope.
Products: FirebaseListObservable<any>;
constructor(){
}
ionViewDidLoad(){
this.angularFire.database.list('/Products').$ref.orderByChild('uid')
.equalTo('NW1Kq4WB7ReUz2BNknYWML9nF133').on('child_added', function(snap){
console.log(snap.val().name);
//this.Products= snap.val();
});
}
I tried the following code when snap is returned ,but I receive this message -- No index defined for uid:
snap.forEach(SnapShot=>{
console.log(SnapShot.val().name)
My Firebase database:
"Products" : {
"-Kbx0i-TFeTyRbNZAZ_8" : {
"category" : "1",
"detail" : "xxxxx details",
"name" : "xxxxx",
"uid" : "NW1Kq4WB7ReUz2BNknYWML9nF133"
}
Please help. Thanks.
The directly answer the question you asked, you can use an ES6 arrow function:
let query = this.angularFire.database.list('/Products').$ref.orderByChild('uid')
.equalTo('NW1Kq4WB7ReUz2BNknYWML9nF133');
query.on('child_added', (snap) => this.Products= snap.val());
Or for ES5 compatibility, declare this as a variable:
let self = this;
let query = this.angularFire.database.list('/Products').$ref.orderByChild('uid')
.equalTo('NW1Kq4WB7ReUz2BNknYWML9nF133');
query.on('child_added', function(snap) {
self.Products= snap.val();
});
But in reality, this is an XY problem and you don't want what you think you want here.
What you've done is reimplement the list yourself, and defeat the entire purpose of AngularFire2, which handles all this synchronization on your behalf.
Additionally, you've mis-used child_added by assigning each record you get back (you get an array of results, not exactly one) to this.products, when you probably wanted to set this.products = [] and then use this.products.push(snap.val()) for each child_added invocation.
So what you really want here, is to use AngularFire's built-in queries and avoid this entire mess :)
this.products = af.database.list('/Products', {
query: {
orderByChild: 'uid',
equalTo: 'NW1Kq4WB7ReUz2BNknYWML9nF133'
}
});
I did it in this way:
import firebase from "firebase";
const firebaseConfig = {
your firebaseConfig...
};
let app = firebase.initializeApp(firebaseConfig);
let database = firebase.database();
export async function readFromFirebase(userId, key) {
const ref = database.ref("users/" + userId + "/" + key);
const snapshot = await ref.once("value");
return snapshot.val();
}
async function main() {
console.log(await readFromFirebase(109512127, "userName"));
}
main();
Any ideas? Why does node say 'filename is undefined'? Thanks.
Contract, policy ans invoice functions resolve with no data, just resolve().
var dc = function(data) {
return new Promise(function(resolve, reject) {
var filename = 'Test';
var contract = function() { ... }
var policy = function() { ... }
var invoice = function() { ... }
contract().then(invoice().then(policy().then(function() {
console.log(filename); // Test
resolve(filename); // UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): ReferenceError: filename is not defined
})))
})
}
First of all, you cannot write:
contract().then(invoice() ... )
(that would work if the invoice() function returned another function to act as a then handler)
You have to write:
contract().then(function (value) { invoice() ... })
Or:
contract().then(value => invoice() ... )
Or maybe this if one function should handle the result of other function:
contract().then(invoice).then(policy).then(function (result) { ... });
What you have to pass as an argument to then is a function, not a result of calling a function (which is probably a promise in your example).
I don't know if that's the only problem with your approach but it is certainly one of the problems. Of course it may work but probably not how you expect.
2017 Update
If you use ES2017 async/await that's available in Node since v7.0 then instead of:
contract().then(invoice).then(policy).then((result) => { ... });
you can use:
let a = await contract();
let b = await invoice(a);
let c = await policy(b);
// here your `result` is in `c`
or even this:
let result = await policy(await invoice(await contract()));
Note that you can only use it in functions declared with the async keyword. This works on Node since version 7. For older versions of Node you can use a similar thing with a slightly different syntax using generator-based coroutines, or you can use Babel to transpile your code if that's what you prefer of if that what you already do.
This is quite a new feature but there are a lot of questions on Stack Overflow about it. See:
try/catch blocks with async/await
Do async in a blocking program language way?
try/catch blocks with async/await
Use await outside async
Using acyns/await in Node 6 with Babel
When do async methods throw and how do you catch them?
using promises in node.js to create and compare two arrays
Keeping Promise Chains Readable
function will return null from javascript post/get
It looks like you don't care about the order, in which case you could use Promise.all. Does this work for you? It will resolve once all of the promises have been resolved, or it will reject as soon as any one of them rejects.
function contract(data) { return new Promise(...) }
function policy(data) { return new Promise(...) }
function invoice(data) { return new Promise(...) }
function dc(data) {
var filename = 'Test';
return new Promise(function(resolve, reject) {
Promise.all([contract(data), policy(data), invoice(data)]).then(
function (values) {
console.log(filename);
resolve(filename)
},
function (err) {
reject(err);
}
);
});
}
If you care about the order, then you do have to chain them, like you've tried to do. You're code is passing promises as an argument to then. You need to pass functions.
function contract(data) { return new Promise(...) }
function policy(data) { return new Promise(...) }
function invoice(data) { return new Promise(...) }
function dc(data) {
var filename = 'Test';
return new Promise(function(resolve, reject) {
contract(data).then(
function (contract_res) {
policy(data).then(
function (policy_res) {
invoice(data).then(
function (invoice_res) {
console.log(filename);
resolve(filename);
},
function (err) { reject(err); } // invoice promise rejected
);
},
function (err) { reject(err); } // policy promise rejected
);
},
function (err) { reject(err); } // contract policy rejected
);
});
}
You may be able to simplify this with catch, but the deep nesting is a pain. Take a look at this post about flattening Promise chains.