Unsuccessfully destructuring the object inside the function - javascript

I have a js file name index.js inside a folder utils
Inside the file:
export const colors = {
PRIMARY_COLOR: "#ff304f",
SECONDARY_COLOR: "#002651",
BORDER_COLOR: "#dbdbdb",
};
I try to destructuring it inside my WeatherInfo component like this:
import React from "react";
import { StyleSheet, View, Text, Image } from "react-native";
import { colors } from "../utils";
export default function WeatherInfo({ currentWeather }) {
const { PRIMARY_COLOR } = colors;
// console.log(colors);
return (
<View>
<Text style={styles.textPrimary}>{temp}</Text>
</View>
);
}
const styles = StyleSheet.create({
textPrimary: {
fontSize: 40,
color: PRIMARY_COLOR,
},
});
I got error of saying Can't find variable: PRIMARY_COLOR, however if I console.log(colors) inside the function, I can see the object printed in the expo console. However, if I do it outside the function, it works. Can someone explain to me what happened?

You are doing it right, but you just misplaced your code it should be outside from function body that's how it will be available on the whole page
const { PRIMARY_COLOR } = colors;
Move it up just one line.

Related

Mass import and loading of images in React with react-three-fiber

I am currently trying to import and use ~300 images (.png files) in my react project. They should be displayed inside a 3D view and loaded using the loader of react-three-fiber.
I have therefore copied the files to a folder named images and created a file index.js which exports the images as follows:
export { default as image_0 } from './0.png';
export { default as image_1 } from './1.png';
[...]
I previously also tried to export the images like this:
import image_0 from './0.png';
import image_1 from './1.png'
[...]
export {
image_0,
image_1,
[...]
}
The images are then loaded in another file:
import * as imageArray from "../../../assets/textures/images"
try {
texture = useLoader(THREE.TextureLoader, imageArray['sign_' + imageNumber]);
} catch (e) {
console.log("Image for number " + imageNumber + " not found.");
}
When testing this workflow without the try/catch I always get the following error inside the developer tools console:
Uncaught Could not load undefined: undefined
I have double checked that the corresponding images are present in images and that there is no naming error. After that I also tried to set all textures to one file which has been declared missing but was definitely inside the folder and therefore loadable:
texture = useLoader(THREE.TextureLoader, imageArray['sign_x']);
I again got the same error and the image could not be loaded.
My guess is that this is a problem caused by too many imported images but I could not find any other solution to import such a high number of files in a project. I would really appreciate any help. Thank you very much in advance!
I dont think the issue is the large imports. I think (Im not certain) that its an import vs require thing. In the docs for expo-three they are using require. I also notice that for images require return an id rather than the files themselves, which may be key to solving your issue.
Here's a demo where I use require to get 130 images, load them with ExpoTHREE.TextureLoader, and then provide them as optional textures for a threejs cube:
import React, { useEffect, useState } from 'react';
import {
Text,
useWindowDimensions,
StyleSheet,
Platform,
Image,
} from 'react-native';
import * as Progress from 'react-native-progress';
import { Asset } from 'expo-asset';
import { TextureLoader, loadAsync, THREE } from 'expo-three';
import Constants from 'expo-constants';
import ThreeWrapper from './components/ThreeWrapper';
import SafeAreaView from './components/SafeAreaView'
import imageArray from './assets/images/index';
global.THREE = global.THREE || THREE;
export default function App({ onComplete, ...props }) {
const [progress, setProgress] = useState(0.0);
const [textures, setTextures] = useState([]);
const { width, height } = useWindowDimensions();
const loadTextures = async () => {
const values = Object.entries(imageArray);
const total = values.length;
const imgs = await Promise.all(
values.map(async ([currentKey, requiredId]) => {
try {
let [{ uri, localUri }] = await Asset.loadAsync(requiredId);
const url = Platform.OS == 'android'
// android needs this?
? uri
:localUri
const texture = new TextureLoader().load(url);
setProgress((prev) => prev + 1 / total);
return texture;
} catch (err) {
console.log(err);
}
})
);
setTextures(imgs);
};
useEffect(() => {
loadTextures();
}, []);
if (Math.round(progress * 1000) / 1000 < 1)
return (
<SafeAreaView style={styles.container}>
<Text style={{ fontSize: 38, fontWeight: 'bold' }}>Loading...</Text>
<Progress.Bar
progress={progress}
width={width * 0.8}
height={36}
borderRadius={50}
/>
<Text
style={{
fontSize: 24,
fontWeight: '300',
textAlign: 'center',
width: '100%',
}}>
{(progress * 100).toFixed(2) + '%'}
</Text>
</SafeAreaView>
);
return (
<SafeAreaView style={styles.container}>
<ThreeWrapper textures={textures} />
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});

How to use vanilla classes in react native?

I'm trying to call a vanilla javascript class inside a component.
This vanilla class is a distributor of cards and i wanted to separate the cards distribution logic from the component.
Where should I instanciate my vanilla class ?
How sure am I of the integrity of this instance (like when the components update) ?
I tried some things like putting in it inside useEffect(()=>{},[]) when the components mount but it didn't work (i didn't have access to my instance), I found this way but it works partially :
import * as WebBrowser from 'expo-web-browser';
import React, { useState, useEffect } from 'react';
import {
Image,
Platform,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
Button,
} from 'react-native';
import BasicMonoSlide from '../components/BasicMonoSlide';
import BasicMonoSlideDistributor from '../models/BasicMonoSlideDistributor';
export default function GameScreen(props) {
const [currentSlide, setCurrentSlide] = useState({});
const [joueurs,setJoueurs] = useState(
JSON.parse(JSON.stringify(props.joueurs))
);
var basicMonoSlideDistributor = new BasicMonoSlideDistributor();
useEffect(()=>{
console.log(currentSlide);
setCurrentSlide(getBasicMonoSlide());
},[]);
useEffect(()=>{
console.log(joueurs);
},[joueurs])
const nextSlide = () => {
console.log("appel next slide");
setCurrentSlide(getBasicMonoSlide());
};
const getBasicMonoSlide = ()=>{
console.log("appel getBasicMonoSlide");
var newSlideData = basicMonoSlideDistributor.getNewSlideData(joueurs,modifyJoueurs,()=>{nextSlide();});
console.log(newSlideData[2]);
return {type:'basicMonoSlide',slide:<BasicMonoSlide questionText={newSlideData[0]} btnText={newSlideData[1]} btnClickHandler={newSlideData[2]}/>};
};
const modifyJoueurs = (index,nom,sexe,orientation,enCoupleAvec,score) => {
var joueursActuel = joueurs;
console.log("modif du joueur a l'index "+index+" "+nom+","+sexe+","+orientation+","+enCoupleAvec+","+score);
const newJoueursArray = joueurs.map((item, indexMap) => {
if (indexMap === index) {
item.index=index;
item.nom=nom;
item.sexe = sexe;
item.orientation=orientation;
item.enCoupleAvec=enCoupleAvec;
item.score=score;
return item;
} else {
return item;
}
});
setJoueurs(newJoueursArray);
}
return (
<View style={styles.container}>
{currentSlide.slide||null}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
My "newSlideData[2]", the onPress given to my slide that has a button is an arrow function defined in my class BasicMonoSlideDistributor calling an arrow function defined in my component and when i click it works 1 or 2 times then does nothing
I know it's long, maybe just answer the first questions :)
probably the slide is attached to the DOM but you donĀ“t see this because you have problems with the CSS, check if in the element is add to the DOM (with the dev tools in your browser) and if is attached, search the problem in the CSS.

React Native and Expo FS import issue

This line:
var a = require('react-native-fs');
returns the following error:
JSX value should be either an expression or a quoted JSX text(53:22)
Thank you for your help.
import React from 'react';
import {
StyleSheet,
View,
Text,
TextInput,
} from 'react-native';
export default class Component1 extends React.Component {
constructor(props) {
super(props);
this.state = {
textInputValue: "",
}
}
render() {
if (!this.props.visible) {
return false;
}
return (
<View
style={styles.component}
>
<View style={styles.layouts}>
<View style={styles.layout1}>
<View style={styles.itemcontainer1}>
<View style={styles.itemcontainer1Inner}>
<View style={styles.item1}>
<TextInput
style={styles.item1TextInput}
placeholder={"b"}
underlineColorAndroid={"transparent"}
placeholderTextColor={"rgba(0,0,0,1)"}
onChangeText={(val) => this.setState({ textInputValue: val })}
value={this.state.textInputValue}
var a = require"react-native-fs";
var path = a.DocumentDirectoryPath + '../textfile.txt';
a.writeFile(path, this.state.textInputValue, 'utf8');
.then((success) => {
console.log('File Written');
JSX is an extension to javascript commonly used by react applications. It looks similar to html tags, with angle brackets at the start and end of elements, and properties on those elements. If you want to put normal javascript inside of JSX, you can do so by using curly brackets. You've got examples of that, such as this one, which has some JSX for the View, then switches back to javascript to pass in styles.layouts:
<View style={styles.layouts}>
You're getting an error because you put javascript code in a random spot, without using curly brackets. Leaving out some of the extra stuff, you wrote:
<TextInput var a = require ("react-native-fs");
Which isn't valid JSX, since you didn't use curly brackets to switch back to javascript. Furthermore, this isn't the right spot to be doing asynchronous stuff, such as writing to a disk. The render method is a synchronous block of code which takes the current state of the component, and returns a description of what you want the screen to look like.
Where to put this code depends on what you're trying to do. If this is something you want to happen when the component is first created, then you'll want to put the code in componentDidMount, which is a function that react components can have to let them run code when they are first mounted. For example:
import fs from 'react-native-fs';
export default class Component1 extends React.Component {
componentDidMount() {
var path = fs.DocumentDirectoryPath + '../textfile.txt';
fs.writeFile(path, this.state.textInputValue, 'utf8')
.then((success) => {
console.log('File Written');
// Possibly call this.setState if you want Component1 to render with something different now that the write is complete
})
}
render() {
// similar render to before, but without the file system code
}
}
Or if this code is supposed to be run when they click a button, you might do it something like this:
onButtonPressed() {
var path = fs.DocumentDirectoryPath + '../textfile.txt';
fs.writeFile(path, this.state.textInputValue, 'utf8')
//etc
}
render() {
return (
// other components omitted for brevity
<Button onPress={() => this.onButtonPressed()}/>
)
}

Es6 Destructuring

I have a file that is similar to this:
const COLORS = {
PRIMARY_COLOR: 'red',
SECONDARY_COLOR: 'green'
};
const APP = {
APP_COLOR: GRAY_DARK,
APP_FONT_SIZE: FONT_SIZE_NORMAL,
APP_FONT_WEIGHT: FONT_WEIGHT_NORMAL,
APP_SEPARATOR_COLOR: GRAY_LIGHT
};
export default {
...COLORS,
...APP
};
The issue is when I'm trying to destructure that object from another file, I get undefined values?
import theme, { PRIMARY_COLOR } from '../../../themes/default';
printing the theme object works fine
but printing PRIMARY_COLOR gets undefined
Any help?
The {} syntax in imports is for "named" imports and is not destructuring.
Just do this:
import theme from '../../../themes/default';
const { PRIMARY_COLOR } = theme;
To understand the difference, you first need to know the way they are exported.
In case of React, the export goes something like this
const Component = ...
...
...
export Component;
This becomes available under React.Component and you can import it like import { Component } from 'react';
The way these look under the microscope is:
default.Component
...
While everything else is just under the default object.
If you do a quick console.log of theme, you'll understand what I mean.
I hope this makes sense.
Let's go a little in depth.
Suppose you have the following bit of code:
const a = {
test: 'hello',
};
const b = {
foo: 'bar',
}
export default a;
Now, let's import that
import * as theme from './test.js'
When we do a console.log( theme ) we get
{ default: { test: 'hello' } }
What does this show? It means that the export object of a file contains a default property which is automatically loaded into memory when we do something like import theme from 'test'. However, if you have more than one export, the compiler gives you the option to pick and choose, but at the same time, provides you with a default object just for fall back.
In your case, you have exported everything under the default. When you do import theme from './theme' all works fine. However, when you do { PRIMARY_COLOR }... it tries to find something which was exported like
export PRIMARY_COLOR...
I hope this makes it clear! :)

React Komposer, react and Meteor

Im Using react Komposer meteor and react .
I have this component
import React from 'react';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
const lightMuiTheme = getMuiTheme(lightBaseTheme);
const Questiondetails = ({ thequestion }) => (
<div>
<MuiThemeProvider muiTheme={lightMuiTheme}>
<h4>{thequestion.header}</h4>
</MuiThemeProvider>
</div>
);
export default Questiondetails;
And this is the container
import { Meteor } from 'meteor/meteor';
import React from 'react';
import { composeWithTracker } from 'react-komposer';
import CircularProgress from 'material-ui/CircularProgress';
import darkBaseTheme from 'material-ui/styles/baseThemes/darkBaseTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import Questiondetails from '../../../ui/components/Questiondetails/Questiondetails.jsx';
import Questions from '../../Collections/Questions/Questions.js';
function composer(props, onData) {
const handle = Meteor.subscribe('singleQuestion', props._id);
if (handle.ready()) {
const thequestion = Questions.findOne({ id: props._id });
onData(null, { thequestion });
}
}
const darkMuiTheme = getMuiTheme(darkBaseTheme);
const MyLoading = () => (<div style={{ width: '90%', position: 'relative' }}>
<MuiThemeProvider muiTheme={darkMuiTheme}>
<div style={{ margin: 'auto', right: 0, left: 0, maxWidth: 200, position: 'relative' }}>
<CircularProgress size={1.0} />
</div>
</MuiThemeProvider>
</div>);
export { MyLoading };
export default composeWithTracker(composer, MyLoading)(Questiondetails);
Im getting Exception from Tracker recompute function:
debug.js:41TypeError: Cannot read property 'header' of undefined
What could I be doing.
When I look over at meteor toys. I get to see the subscription in the component.
This is my publication
import { Meteor } from 'meteor/meteor';
// import the db
import Questions from '../../../../api/Collections/Questions/Questions.js';
// the publish
Meteor.publish('singleQuestion', function(id){
return Questions.find({ _id: id });
});
It is likely that you don't get the data record.
Even after the subscription handle is ready, it is possible that the query returns undefined, either because there is no data in the collection or if your query is wrong.
In this case, it seems that the query is indeed wrong, leading to you passing undefined to the component instead of the expected object.
If you provide a string as the first argument to find() or findOne(), it is assumed that you mean the _id, so it prevents errors like the (common) one you made (Questions.findOne({ id: props._id }), using the id key instead of _id).
You can use the error argument in order to catch such cases more easily (and show a meaningful error message in case something is actually wrong).
I also suggest changing thequestion to simply be question or theQuestion (more readable), unless there is a very good reason not to.
function composer(props, onData) {
const handle = Meteor.subscribe('singleQuestion', props._id);
if (handle.ready()) {
const question = Questions.findOne(props._id);
let error = null;
if (!question) {
error = new Error('no question matches the provided id');
}
onData(error, {question});
}
}

Categories