MobX passing simple array from observable to observer - arrays

I am new to MobX and was recommended it here.
I have a simple array which I am trying to pass to another file.
I have tried many things but I am unable to receive the array in the form of this.props.store
If you could shine some light on my improper use of mobX it would be greatly appreciated. Thank you.
import React, { Component } from 'react';
import {
Alert,
AppRegistry,
StyleSheet,
Text,
TouchableHighlight,
View
} from 'react-native';
import styles from '../styles/styles.js';
import {observable, extendObservable} from 'mobx';
//unsure why below code wont work, when trying to export it throws error: object is not a constructor (evaluating 'new rNumStore()')
// const rNumStore = observable([1,2,3]);
// a couple of other options both i am unsure why they do not work
// class rNumStore {
// extendObservable(this, {
// rNumbers: [1,2,3]});
// }
// class rNumStore {
// observable({rNumbers: [1,2,3]});
// }
//Current best working solution below
var rNumStore = function(){
extendObservable(this, {
rNumbers: [1,2,3]
});
}
var Button = React.createClass({
rNumberGen: function(){
store.rNumbers = [Math.random(), Math.random(), Math.random()];
var a = store.rNumbers[0].toString();
var d =store.rNumbers.toString();
Alert.alert(a, d);
//this alert gives mobxArray of random numbers
},
render: function(){
return(
<TouchableHighlight onPress={this.rNumberGen} style= {styles.center}>
<Text style={styles.button}>Generate!</Text>
</TouchableHighlight>
);
}
});
var store = new rNumStore;
export default store;
export {Button};
Then the second file needs to observe the array
import React, { Component } from 'react';
import {
Alert,
AppRegistry,
StyleSheet,
TouchableHighlight,
Text,
View
} from 'react-native';
import{
genre1,
genre2,
genre3
} from './genres.js';
import styles from '../styles/styles.js';
import {observer} from 'mobx-react/native';
var Genre = observer(React.createClass({
alert: function(){
//below does not cause an error.
var a = this.props.store;
//undefined ins not an object(evaluating 'this.props.store.rNumbers')
// have tried making 'a' a state : this.props.store.rNumbers same error occurs at that point
var b = this.props.store.rNumbers[1];
var c = b.toString();
Alert.alert('this is', c);
},
render: function(){
let genre = this.props.selected;
return(
<TouchableHighlight onPress={this.alert}>
<View style={styles.genre}>
<Text style={styles.center}>{genre}</Text>
</View>
</TouchableHighlight>
);
},
}));
module.exports = Genre;

Where is your main app code? That should create the store, then in it's render it should have and tags that you pass the store to as a property
Edit:
The main store needs a class:
class rNumStore {
rNumbers = observable([1,2,3])
rNumGen() {
this.rNumbers = [Math.random(), Math.random(), Math.random()]
//Alert.alert('this is', rNumbers.toString())
}
}
class Main extends React.Component {
render(){
return(
<View style={styles.container}>
<Text style={styles.title}>Genre Genrerator</Text>
<Text style={[styles.h2, styles.h21]}>I am listening to</Text>
<View style={styles.genreContainer}>
<Genre store={store} selected='{genre[1]}'/>
<Genre store={store} selected='{genre[2]}'/>
<Genre store={store} selected='{genre[3]}'/>
</View>
<Text style={styles.h2}>You filthy casual</Text>
<Button store={store}/>
</View>
);
}
}
var store = new rNumStore;
export default store;
module.exports = Main;
which is exported as above.
Then as Peter says the store then needs to be passed into the props of the observer classes.
Thanks for the help.

Related

How can i use a function as a rxjs element in React Native?

I have a separated function class that is taking parameter and which this parameter doing some process and finally returns a flatlist by this processed data. I want to call this function in my App.js and use it as a render element. How can I do that?
Here is my ViewPhoto class
import React, {Component} from 'react';
import {
View,
Platform,
FlatList,
Image,
} from 'react-native';
import {styles} from './Styles';
const ViewPhoto = ({imageData}) => {
let imageList = [];
for (let i = 0; i < Object.keys(imageData).length; i++) {
console.log('saymaya basladi');
let data = imageData[String(i)];
let image = {
id: String(i),
contentType: data.mime,
fileSize: data.size,
filePath: data.path,
};
if (Platform.OS === 'ios') {
image.fileName = data.filename;
} else {
let path = data.path.split('/');
image.fileName = path[path.length - 1];
}
imageList.push(image);
}
return (
<View style={styles.imageViewerContainer}>
<View style={styles.imageContainer}>
<FlatList
data={this.state.imageList}
numColumns={2}
renderItem={({item}) => (
<Image style={styles.image} source={{uri: item.filePath}} />
)}
keyExtractor={item => item.id}
/>
</View>
</View>
);
};
And this is my App.js
import React from 'react';
import {ViewPhoto} from './ViewPhoto';
export default class App extends React.Component{
constructor() {
super();
this.state = {
profilePic:[]
}
}
render() {
return (
<View>
<ViewPhoto imageData={this.state.profilePic}></ViewPhoto>
</View> )}
}
When I use it this was i get this error.
Unhandled JS Exception: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Looks you missed exporting the functional component. Change the below line in your ViewPhoto:
const ViewPhoto = ({imageData}) => {
to
export default const ViewPhoto = ({imageData}) => {

Not able to use map function react-native

I am new to react native and I am using Async storage to store objects and then parse, by storing them in favorites array. I am trying to map each element in array and display it.
I get this error: this.state.favorites.map is not a function.
I think it's a state error but as I'm new to these concepts, i don't know how to fix it. Please help.
import React, {Component} from 'react';
import {StyleSheet, Text, View, Image, FlatList, Button, AsyncStorage,
TouchableOpacity} from 'react-native';
import ajax from './ajax.js';
import PropTypes from 'prop-types';
import InspirationList from './InspirationList';
import FavoriteItem from './FavoriteItem';
import ProductItem from './ProductItem';
class Favorites extends Component{
state = {
favorites:[],
};
componentDidMount(){
this.showProduct();
}
async showProduct() {
let k=0;
AsyncStorage.getAllKeys()
.then(keys => AsyncStorage.multiGet(keys)
.then((result) => {
result.map(req => req.forEach((element) => {
k++;
if(element!=null && k%2==0){
this.setState({favorites: JSON.parse(element) });
console.log(this.state.favorites.nume);
}
}));
}));
};
render(){
return(
<View style='styles.fav'>
{this.state.favorites.map((fav)=>
<Text>{fav.nume}</Text>
)}
</View>
);
}
}
const styles = StyleSheet.create({
fav:{
backgroundColor:'#999',
flex:1,
width:'100%',
paddingTop:150,
}
});
export default Favorites;
On your showProduct function you are overriding what your state originally was. When assigning the JSON.parse(element) you are changing your state instead of having an array your are converting it into an object, that's why you are getting that error. map exists only on array-like.
So maybe you can do something like:
async showProduct() {
let k=0;
AsyncStorage.getAllKeys()
.then(keys => AsyncStorage.multiGet(keys)
.then((result) => {
result.map(req => {
let result = [];
req.forEach((element) => {
k++;
if(element!=null && k%2==0){
result.push(JSON.parse(element))
}
});
this.setState({favorites: result });}
);
}));
};

Export and import observable MobX React Native

I am new to React Native and very new to MobX, only realising i needed it when my first project demanded dynamically updating and changing props/store between files.
Here is the github of the project: https://github.com/Kovah101/GenreGeneratorv1
I am trying to build an app that generates the name of random genre of music.
My main file renders all the components and has a small console.log to check that random numbers are being generated. I don't get any errors here
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import
GenreSelector
from './genreSelector';
import GenerateButton from './generateButton';
import styles from '../styles/styles';
import randomStore from './generateButton';
var MainOriginal = React.createClass ({
getInitialState() {
return {
chosenRandoms: [ , , ],
};
},
//used to check Generate button is working
changeRandoms(newRandoms) {
this.chosenRandoms = newRandoms;
console.log('the console log works!');
console.log(this.state.chosenRandoms);
} ,
render(){
return (
<View style={styles.container}>
<Text style= {styles.title}>Genre Generator</Text>
<Text style={styles.h2}>I am listening to</Text>
<View style={styles.genreContainer}>
<GenreSelector store={randomStore}/> {//passing randomStore as a prop to selector
}
</View>
<Text style={styles.h2}>You filthy casual</Text>
<GenerateButton onPress={this.changeRandoms}/>
</View>
);
}
});
module.exports = MainOriginal;
Next, GenerateButton renders a button with an onClickevent that generates an array of random numbers, these get checked by mainOriginaland work correctly. I also use MobX to make randomNumbers observable as it is constantly updated and will be passed to the final file genreSelector.
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
TouchableHighlight,
Text,
View
} from 'react-native';
import styles from '../styles/styles';
import {observable} from 'mobx';
var GenerateButton = React.createClass({
generateRandoms: function() {
#observable randomNumbers = [Math.random(), Math.random(), Math.random()];
this.props.onPress(randomNumbers);
},
render: function (){
return (
<TouchableHighlight
onPress={this.generateRandoms}
style={styles.generateButton}>
<Text style={styles.generateButton}>Generate!</Text>
</TouchableHighlight>
);
}
});
const randomStore = new GenerateButton ();
export default randomStore;
module.exports = GenerateButton;
genreSelector should use the array of random numbers map them to the size of the 3 different genre arrays, then render 3 boxes, each with one of the random genres from each array. However I get unexpected tokens at 'chosenRandoms' if i set it to be a 'var' and the same again at 'get randomGenres`, my understanding is they need to be something.
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import{
genre1,
genre2,
genre3
} from './genres.js';
import styles from '../styles/styles';
import {observer, computed} from 'mobx-react/native';
import randomStore from './generateButton';
const size = [genre1.length, genre2.length, genre3.length];
#observer
class GenreSelector extends Component {
render() {
var chosenrandom = this.props.randomStore.randomNumbers; //accessing the passed state
console.log({chosenrandom});
let randomGenres = [Math.floor(this.chosenrandom[0] * size[0], Math.floor(this.chosenrandom[1] * size[1], Math.floor(this.chosenrandom[2] * size[2]],
//manipulating the passed array -- ERROR AT END OF THIS LINE
return (
<View style={styles.genreContainer}>
<Text style={styles.genreText} >{genre1[randomGenres[0]]}</Text>
<Text style={styles.genreText} >{genre2[randomGenres[1]]}</Text>
<Text style={styles.genreText} >{genre3[randomGenres[2]]}</Text>
</View>
);
}
}
module.exports = GenreSelector;
Does anybody have any ideas on what i'm doing wrong? If i take the var and get out then i get an error at the end of the math manipulation line. I must be misusing something. Thanks for any help, I can provide more of my code but i dont think the problem is in the stylesheet or index.
So i decided to put all the calculations and genre picking to the generatebuttonand just send the array as a prop from the button to the mainOriginal then back to genreselector.
Didn't have to use MobX or anything overly complicated.
Here is the final code: github.com/Kovah101/GenreGeneratorv2

React Native passing array between components

I am trying to pass an array of numbers from one child component to another. I believe the way to do this is through their parent file.
First child- generateButton simply generates an array of 3 random numbers onPress
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
TouchableHighlight,
Text,
View
} from 'react-native';
import styles from '../styles/styles';
var GenerateButton = React.createClass({
generateRandoms: function() {
let randomNumbers = [Math.random(), Math.random(), Math.random()];
this.props.onPress(randomNumbers);
},
render: function (){
return (
<TouchableHighlight
onPress={this.generateRandoms}
style={styles.generateButton}>
<Text style={styles.generateButton}>Generate!</Text>
</TouchableHighlight>
);
}
});
module.exports = GenerateButton;
Parent- main uses this to change the value of the state randomGenres to the value returned by generateButton, this state is then passed to the 2nd child Genre
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import {
Genre
} from './genreSelector';
import GenerateButton from './generateButton';
import styles from '../styles/styles';
var Main = React.createClass ({
propTypes: {
randomGenres: React.PropTypes.array,
newRandoms: React.PropTypes.array
},
getInitialState: function () {
return { randomGenres: [4,7,19]};
},
changeRandoms: function (newRandoms) {
this.setState({
randomGenres: newRandoms
});
},
render (){
return (
<View style={styles.container}>
<Text style= {styles.title}>Genre Generator</Text>
<Text style={styles.h2}>I am listening to</Text>
<View style={styles.genreContainer}>
<Genre randomGenres={this.state.randomGenres}/>
</View>
<Text style={styles.h2}>You filthy casual</Text>
<GenerateButton onPress={this.changeRandoms}/>
</View>
);
}
});
module.exports = Main;
Second child- Genre, at this point all i want it to do is receive randomGenres as a prop and render the three values
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import{
genre1,
genre2,
genre3
} from './genres.js';
import styles from '../styles/styles';
var Genre = React.createClass({
render: function (){
var randomGenres = this.props.randomGenres;
return (
<View styles={styles.genreContainer}>
<Text >{randomGenres[0]}</Text>
<Text >{randomGenres[1]}</Text>
<Text >{randomGenres[2]}</Text>
</View>
);
}
});
module.exports = Genre;
Eventually it will convert the random numbers into random genres by finding a random element of an array of genres and displaying them instead of just the numbers. However i cannot seem to pass the array between the components
My Error: Element type is invalid: expected a string or a class/function, check the render method of main
This only occurs when this line is uncommented:
<Genre randomGenres={this.state.randomGenres}/>
I believe my error lies in how Genre receives the array stored in randomGenres
I have tried to set the data type of the prop using propTypes but no luck, any help would be greatly appreciated!
EDIT:
Within the render method of your Genre component you use the attribute styles instead of style Removing it should fix
your error:
import React, { Component } from 'react';
...
var Genre = React.createClass({
render: function (){
var randomGenres = this.props.randomGenres;
return (
<View style={styles.genreContainer}>
...
</View>
);
}
});
module.exports = Genre;
As NiFi's comment below details: the setState method accepts a callback as an optional second argument which is triggered once setState has completed. Grabbing the randomNumbers value here should yield the correct result

React Native: this.state of reusable components not expected

I'm a newbie of React native and honestly I have just a very basic knowledge of React. I'm developing a sample application in which I make use of reusable components and ES6 sintax.
I'am experiencing unexpected results when reusing the same component multiple times in the same Scene (I also make use of Navigator). More precisely I can't understand why differents components (of the same type) are apparently conditions each others states.
I'm posting my code for a better understanding.
This is my main page , in which I make use 2 times of the same custom defined component < TopCategories /> :
HomeScene.js
import React from 'react';
import {View} from 'react-native';
import BaseScene from './BaseScene'
import SearchSuggest from '../components/SearchSuggest';
import TopCategories from '../components/TopCategories'
import styles from '../styles'
export default class HomeScene extends BaseScene {
render() {
return(
<View style={styles.container}>
<SearchSuggest
navigator={this.props.navigator}
/>
<TopCategories/> //first
<TopCategories/> //second
</View>
)
}
}
These are the details of the inner components used:
TopCategories.js
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet
} from 'react-native';
import styles from '../styles'
import utility from '../utility'
import serverConnector from '../serverConnector'
import routes from '../routes'
import MenuItemComplex from './MenuItemComplex'
export default class TopCategories extends Component {
constructor(props) {
super(props);
this.state = {categories: []};
this._fetchContent();
}
_updateCategoriesList(rawCategories){
//store in state.categories array a simplied version of
//the contents received from the server
let simplifiedCategories = [];
for(i=0; i<rawCategories.length; i++){
var simpleCat = {};
simpleCat.id = rawCategories[i].uniqueID;
simpleCat.name = rawCategories[i].name;
simplifiedCategories.push(simpleCat);
}
this.setState({categories: simplifiedCategories });
}
_fetchContent(){
//fetch content from server in JSON format
_topCategories = this;
serverConnector.call(
"CATEGORY",
"FindTopCategories",
{},
function(err, json){
if(err!=null) utility.log("e", err);
else {
try{
_topCategories._updateCategoriesList(json.res.header.body.CatalogGroupView);
}catch(err){
utility.log("e", err);
}
}
}
)
}
openCategoryScene(id, name){
//push on Navigator stack the next route with additional data
let nextRoute = routes.get("categoriesListFirst");
nextRoute.passProps = {
categoryId: id,
categoryName: name
};
this.props.navigate(nextRoute)
}
render(){
console.log(this.state)
return (
<MenuItemComplex key="categories" name="Catalogo" icon="list-b" onItemSelected={this.openCategoryScene.bind(this)} subItems={this.state.categories} />
)
}
}
and finally
MenuItemComplex.js
import React, { Component } from 'react';
import { View, Text, Image, TouchableHighlight, TouchableWithoutFeedback } from 'react-native';
import styles from '../styles'
export default class MenuItemComplex extends Component{
static propTypes = {
name : React.PropTypes.string.isRequired,
icon : React.PropTypes.string.isRequired,
subItems: React.PropTypes.array.isRequired,
onItemSelected: React.PropTypes.func.isRequired
};
render(){
let subItems = [];
for(i=0; i<this.props.subItems.length; i++){
let subItem = this.props.subItems[i];
subItems.push(
<TouchableHighlight
key={subItem.id}
underlayColor={"#d00"}
activeOpacity={1}
onPress={() => this.props.onItemSelected(subItem.id, subItem.name)}
>
<View style={styles.menuSubItem}>
<Text style={[styles.mmText, styles.menuSubItemText]} >
{subItem.name}
</Text>
</View>
</TouchableHighlight>
)
}
return(
<View>
<TouchableWithoutFeedback disabled={true}>
<View style={styles.menuItem}>
<Image style={styles.menuItemImage} source={{uri: this.props.icon}} />
<Text style={[styles.mmTextBold, styles.menuItemText]}>{this.props.name}</Text>
</View>
</TouchableWithoutFeedback>
{subItems}
</View>
)
}
}
I can't understand why the state.simplifiedCategories of the first < TopCategories > component used in my HomeScene seems to be an empty array after the second < TopCategories > component has rendered. So far I thought that the two components were completely isolated, with their own "private" state. But in this case seems that this is shared somehow.
Can someone explain what is happening here ? And then how can I fix that ?
Thanks
EDIT 2016/09/05
As suggested by user V-SHY I tried to give every component a randomic string as key, but this does not solve the problem.
What I find very strange is that I can see only an instance of < TopCategories > in the global window object, the last one.
The screenshot here refers to a test made with
<TopCategories key="tc_first" {...this.props}/>
<TopCategories key="tc_second" {...this.props}/>
in the HomeScene.js file
As suggested by Daniel there was an issue during the fetch of data from server. In particular I was wrong when creating a _topCategories=this object inside TopCategories.js file
_fetchContent(){
_topCategories = this; // ISSUE HERE!
serverConnector.call(
"CATEGORY",
"FindTopCategories",
{},
function(err, json){
if(err!=null) utility.log("e", err);
else {
try{
_topCategories._updateCategoriesList(json.res.header.body.CatalogGroupView);
}catch(err){
utility.log("e", err);
}
}
}
)
}
I solved passing the fetchContent method a reference of the component :
constructor(props) {
super(props);
this.state = {categories: []};
this._fetchContent(this); // passing THIS reference
}
_updateCategoriesList(rawCategories){
let simplifiedCategories = [];
for(i=0; i<rawCategories.length; i++){
var simpleCat = {};
simpleCat.id = rawCategories[i].uniqueID;
simpleCat.name = rawCategories[i].name;
simplifiedCategories.push(simpleCat);
}
this.setState({categories: simplifiedCategories});
}
_fetchContent(instanceRef){
motifConnector.call(
"CATEGORY",
"FindTopCategories",
{},
function(err, json){
if(err!=null) utility.log("e", err);
else {
try{
instanceRef._updateCategoriesList(json.res.header.body.CatalogGroupView);
}catch(err){
utility.log("e", err);
}
}
}
)
}

Resources