I am trying to connect to Contentful's API in my react project. If I hardcode the space and access tokens it works fine, but if I try to use a .env file it's not getting the token.
Here's my code:
import {createClient} from "contentful";
export const client = () => {
//console.log(process.env)
let whatever = createClient({
space: process.env.REACT_APP_SPACE_ID ,
accessToken: process.env.REACT_APP_ACCESS_TOKEN,
});
return whatever;
};
export const getRecipes = async () => {
try{
const entries = await client().getEntries({
content_type: "recipe",
select: "fields"
});
return entries;
} catch(error){
console.log(`error fetch: ${error}`);
return;
}
}
I've tried various patches but nothing has worked. I have react-scripts installed as well.
I console logged the process.env and it doesn't have the token or space id.
I tried importing dotenv but that created some weird polyfill error that I've never seen before.
It could be that: your env file is not in the project folder but instead in the src/ folder...just check it once to make sure, it's a very easy mistake that a lot of people make.
Or if you have the client folder inside the server folder which is sometimes needed for deployment it might not be working because of that folder structure as well
Related
hay guys, i am making a react native app with expo and i am using "expo document picker" module to access the folder in the device by clicking on the button in my app , i am getting this error in my terminal --
LOG [Error: Encountered an exception while calling native method: Exception occurred while executing exported method getDocumentAsync on module ExpoDocumentPicker: No Activity found to handle Intent { act=android.intent.action.OPEN_DOCUMENT cat=[android.intent.category.OPENABLE] typ=directory }]
this is my code---
const pickDirectory = async () => {
console.log("hi!!!!!");
try {
const permission =
await FileSystem.StorageAccessFramework.requestDirectoryPermissionsAsync();
if (permission.granted) {
const dir = await DocumentPicker.getDocumentAsync({
copyToCacheDirectory:true,
type: "directory",
});
// setSyncDirectory();
console.log(dir);
}
} catch (error) {
console.log(error);
}
};
The error you are seeing is because the DocumentPicker.getDocumentAsync() method can't find an app on your device that can open a folder.
The DocumentPicker module is meant for selecting and opening documents, not folders. If you want to let users choose a folder, you can use FileSystem.StorageAccessFramework instead. This allows you to request access to a directory and perform file operations like reading and writing files.
import * as FileSystem from 'expo-file-system';
const pickDirectory = async () => {
console.log("hi!!!!!");
try {
const permission = await FileSystem.requestDirectoryPermissionsAsync();
if (permission.granted) {
const dir = await FileSystem.StorageAccessFramework.browseForFolderAsync();
console.log(dir);
}
} catch (error) {
console.log(error);
}
};
Note that the browseForFolderAsync() method is currently only available on Android, so if you need to support iOS as well, you will need to use a different method or library to allow the user to select a folder.
I'll preface by saying that I'm new to React, so hopefully there's a simple answer that I just missed somewhere.
In short, I want to build a page that I can run without any kind of webserver. I have everything configured so I can just open index.html and it runs fine.
The problem, however, is that I want a data.json file that can be edited after the project is built (essentially a config file) so I can't just stick it inside the /src folder because it all gets bundled.
data.json
{
"timeout": 10,
"threshold": 50
}
import React from 'react';
const App = () => {
const path = process.env.PUBLIC_URL + '/data/data.json';
fetch(path)
.then((res) => res.text())
.then((json) => {
const data = JSON.parse(json);
})
return (
...
);
}
This may work fine when running in VSCode, but throws CORS errors when running the index.html page by itself. I also know that I can't just reference the a file directly in /public because it's outside the /src folder:
import data from '../Public/data/data.json';
How can I have a configurable file in /Build that I can read in from React without running into CORS issues.
Hopefully this question makes sense
Thanks
There is no need to prepend the PUBLIC_URL env variable.
This should work
fetch('/data/data.json')
I was able to workaround this problem by injecting the configs into the window object in the index.html file. It's pretty hacky but it allows me to edit the index.html file and configs after the projects been built. I'd love to hear some better solutions.
index.html
<script>
window.__data = {
"timeout": 10,
"threshold": 50
}
</script>
App.tsx
const App = () => {
const data = (window as any).__data;
return (
...
);
}
I've installed several libraries using NPM.
You have to load the file to use it there.All installed libraries are under the node_modules
The designation is like this.
<script src="../node_modules/vue/dist/vue.js"></script>
I'm sure it's on the server, I'm sure the URL of the script tag is correct
And I get the following error in the script, even though it is specified like this
login.html:45 GET https://***/node_modules/vue/dist/vue.js net::ERR_ABORTED 404
The JS file I prepared separately is loaded, but the original vue.js is not, so an error is coming back.
That's what we're trying to do with VUE.
I'm going to use Ajax to create an SPA
/* eslint-disable no-console */
const vue = require("vue"); //I added it, but it's still not working.
const axios = require("axios"); //I added it, but it's still not working.
var app = new Vue({
el: "#app",
data: {
url: "https://*****",
user: "",
password: "",
res: ""
},
methods: {
login: function () {
axios.post(this.url, {
user: this.user,
password: this.password
})
.then(function (response) {
this.res = response.data.d;
if (this.res === true) {
//true
} else {
//failed
}
})
.catch(function (error) {
alert('System error');
});
}
}
})
This is how usually use it from node_module
import Vue from 'vue'; // this will check inside node_modules for the file and include
Instead of loading the files through a script tag, use require() in your javascript. For example:
const express = require("express");
const vue = require("vue");
This will work on both the client and on the server.
Why not use a CDN?
This way, the browser will not have to download the Vue file from your server if it has already been cached. Also, you may be able to use the vue.min.js file which will load even faster than the normal Vue dist file...
For example, here is a script from cdnjs.com you could try:
<script
src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"
integrity="sha256-ngFW3UnAN0Tnm76mDuu7uUtYEcG3G5H1+zioJw3t+68="
crossorigin="anonymous"></script>
Try replacing your script tag with this.
According to the expo sqlite documentation for react-native I can initialize a db like so:
const db = SQLite.openDatabase('db.db');
This works and I can update the db like so:
update() {
db.transaction(tx => {
tx.executeSql(
`select * from items where done = ?;`,
[this.props.done ? 1 : 0],
(_, { rows: { _array } }) => this.setState({ items: _array })
);
});
}
From my limited understanding this creates a database in the device. And then it's manipulated keeping all the db local.
I have a database with all the necessary tables already setup. How can I have it use the current database I already have setup?
For example: (not correct syntax)
const db = SQLite.openDatabaseIShipWithApp('mypath/mydb.db');
I couldn't find any documentation to help me with this.
The only reason I mention the above is because I already have the db with the tables and data.
Any help would be appreciated!
I was able to achieve this by using expo's FileSystem.downloadAsync:
first I import it since I'm using expo managed app:
import { FileSystem } from 'expo';
Then I download it from a server like so:
// load DB for expo
FileSystem.downloadAsync(
'http://example.com/downloads/data.sqlite',
FileSystem.documentDirectory + 'data.sqlite'
)
.then(({ uri }) => {
console.log('Finished downloading to ', uri)
})
.catch(error => {
console.error(error);
})
The first parameter is the uri for the location, the second one is where I'd like to place it. Here I am using documentDirectory.
If using local prepopulated database in assets:
import * as FileSystem from "expo-file-system";
import {Asset} from "expo-asset";
async function openDatabaseIShipWithApp() {
const internalDbName = "dbInStorage.sqlite"; // Call whatever you want
const sqlDir = FileSystem.documentDirectory + "SQLite/";
if (!(await FileSystem.getInfoAsync(sqlDir + internalDbName)).exists) {
await FileSystem.makeDirectoryAsync(sqlDir, {intermediates: true});
const asset = Asset.fromModule(require("../assets/database/mydb.sqlite"));
await FileSystem.downloadAsync(asset.uri, sqlDir + internalDbName);
}
this.database = SQLite.openDatabase(internalDbName);
}
This creates the SQLite directory and database if not exists. Otherwise FileSystem.downloadAsync() will throw an error on fresh installed app.
Some remarks:
You cannot use variable in require() (only string). See e.g. this.
You have to explicitly allow file extension .db or .sqlite to be loadable in Expo, see this. You have to create a file metro.config.js in root:
const defaultAssetExts = require("metro-config/src/defaults/defaults").assetExts;
module.exports = {
resolver: {
assetExts: [
...defaultAssetExts,
"db", "sqlite"
]
}
};
And may add following to app.json
"expo": {
"assetBundlePatterns": [
"**/*"
]
}
If want to delete loaded database (e.g. for testing) you have to clear whole Expo App data in Phone settings (deleting cache not sufficient). Or write a method like this:
async function removeDatabase() {
const sqlDir = FileSystem.documentDirectory + "SQLite/";
await FileSystem.deleteAsync(sqlDir + "dbInStorage.sqlite", {idempotent: true});
}
It's pretty straight forward
If you bundle your app, you have to move the Database from the asset folder to the document directory first. In order to do that, check if a folder named SQLite exists. If not, create it. Why do you need a folder called SQLite? That is because SQLite.openDatabase(databaseName) looks per default in FileSystem.documentDirectory + 'SQLite'. Then, when the folder is created, you can download the database from the asset folder. Make sure you have your database in a folder called asset. Locate the foler asset under src/asset of your app document tree. Also, make sure to configure your app.json and metro.config.js.
import * as SQLite from 'expo-sqlite';
import * as FileSystem from 'expo-file-system';
import { Asset } from 'expo-asset';
const FOO = 'foo.db'
if (!(await FileSystem.getInfoAsync(FileSystem.documentDirectory + 'SQLite')).exists) {
await FileSystem.makeDirectoryAsync(FileSystem.documentDirectory + 'SQLite');
};
await FileSystem.downloadAsync(
// the name 'foo.db' is hardcoded because it is used with require()
Asset.fromModule(require('../../asset/foo.db')).uri,
// use constant FOO constant to access 'foo.db' whereever possible
FileSystem.documentDirectory + `SQLite/${FOO}`
);
// Then you can use the database like this
SQLite.openDatabase(FOO).transaction(...);
// app.json
{
"name": "Your App name",
"displayName": "Your App name",
"assetBundlePatterns": [
"assets/**"
],
"packagerOpts": {
"assetExts": ["db"]
}
}
// metro config
const { getDefaultConfig } = require('#expo/metro-config');
const defaultConfig = getDefaultConfig(__dirname);
module.exports = {
resolver: {
assetExts: [...defaultConfig.resolver.assetExts, 'db', 'json'],
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
};
This is all extracted from the documentation of expo.
I don't believe this is possible in expo. There is a way to use an existing database if you are using a bare android project which involves writing some native code to copy the database from the project assets to the standard location on the phone (/data/ etc) for your application.
https://medium.com/#johann.pardanaud/ship-an-android-app-with-a-pre-populated-database-cd2b3aa3311f
I have always personally created the database myself with CREATE TABLE IF NOT EXISTS since sqlite requires you to define the schema before you query it. If you need to seed the database, this step would then be followed by additional steps to insert the required data.
In most cases, you will need to also check your reference data and update it from the server at regular intervals (it might even change from publishing your apk to someone downloading the app) and this code would also work when there is no reference data in the database.
There are a couple of services which try and take his hassle away from you (e.g. parse) but you will need to decide if you are happy with them hosting your data.) I haven't used them so not sure how this works exactly but I'm told it tries to solve the offline first type problems.
Remember that in future iterations you may need to modify the structure for future versions (to add fields etc) so you will probably need to define some code that loads when the application is first started, checks the database version and applies any changes that are required to bring the database up to the appropriate level.
I want to display the current git tag on my app's login page,
Its built using react.
Im trying to use the 'git-rev-sync' library to do this.
but it doesnt seem to work on the client side because I keep getting errors like
'cannot find module 'children process', it works on the server side where Im able to console.log and print the tag
anyone know how to achieve this? Open to any solutions with any library
import version from 'git-rev-sync'
...
class Login extends Component {
...
render ()
...
return (
<div> my version: {version.tag()} </div>
) }
Thanks
I decided to use git-revision-webpack-plugin which creates a VERSION file (among other files) in the dist folder, and then I read the file from my client side react app:
add this to your webpack.js:
const GitRevisionPlugin = require('git-revision-webpack-plugin')
module.exports = {
plugins: [
new GitRevisionPlugin({
lightweightTags: true //I added this to get the tags as well
})
]
}
then my client side looks like this:
const [revision, setRevision] = useState('')
const fetchRevision = async () => {
let result = await fetch('/dist/VERSION')
let txt = await result.text()
txt = txt.replace(/^(.*?)(?:\-.*)?$/, '$1') //I only care for the tag.
setRevision(txt)
}
useEffect(() => {
fetchRevision()
}, [])
and then you can render the revision
One thing to notice, depending on your server, you may need to tell it to serve this VERSION file as is, so for example in express, you might find you need this:
server.get('*', (req, res, next) => {
if (/^\/dist\/*/.test(req.originalUrl)) {
const relative = req.originalUrl.replace(/\/dist(\/.*)/, '$1')
const filename = path.join(compiler.outputPath, relative)
compiler.outputFileSystem.readFile(filename, (err, result) => {
if (err) {
return next(err)
}
res.send(result)
res.end()
})
}
...
})
Hope this helps for future use.
If you used create-react-app#0.2.3 > to generate your app.
create-react-app scripts use environment variables that start with the REACT_APP_ symbol in the root .env file. create-react-app - Adding custom environment variables is a good place to dig into the details.
or just include the following in your .env file.
.env
REACT_APP_VERSION=$npm_package_version
and access it on your react login component by referring to {process.env.REACT_APP_VERSION}