MobX observable not refreshing in React Native - react-native

I am trying to hot refresh observable data in a react native view. The initial value is being displayed perfectly but does not refresh when I call an action from the store to change it. It is, however, being changed (I have another view where I can see the change) just not on the screen that calls the action to update. Any ideas? I am using React Navigation. Not sure if that is somehow interfering.
Store.js
import { observable, action, decorate, computed } from 'mobx'
class BooksStore {
bookColor = 'green'
testAction = () => {
console.log('change store value')
this.bookColor = 'blue'
}
}
decorate(BooksStore, {
loading: observable,
testAction: action,
});
export default new BooksStore();
App.js
import React from 'react';
import Routes from './Routes';
import { Provider } from 'mobx-react';
import BooksStore from './src/Stores/BooksStore';
export default function App() {
return (
<Provider booksStore={BooksStore}>
<Routes /> //react-navigation stacks
</Provider>
);
}
RN View:
import React, { Component } from 'react';
import { Image, View, Button, Alert, ScrollView, Dimensions, StyleSheet, Text } from 'react-native';
import { inject, observer, Observer} from 'mobx-react'
class MobX1 extends Component {
render() {
console.log(this.props.booksStore.bookColor)
return (
<View style={styles.container}>
//this displays initial store val but no refresh when I call testAction()
<Observer>{() =>
<Text style={{ marginBottom: 48 }}>{this.props.booksStore.bookColor}</Text>
}</Observer>
<Button
title="Change to Blue"
onPress={() => this.props.booksStore.testAction()} />
</View>
);
}
}
export default inject('booksStore')(observer(MobX1));
//------------------------------ styles -------------------------------//
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
Thanks for any help

Anyone that comes across this... I forgot to decorate properly... durrrrr
decorate(BooksStore, {
bookColor: observable, <<<<<<<<<<<<<
testAction: action,
});

Related

How to Use method of functional component into class component in react native?

I am working in one react native project in which, I want to make common component for show loading indicator (for inform user to wait until process done.)
For that , I have make one js file that is common for my project
Look like below :
Loader.JS : Common functional component in react native
import React, {useState} from 'react';
import {View, StyleSheet, ActivityIndicator} from 'react-native';
import {loaderColor} from './app.constants';
const Loader = () => {
return (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={loaderColor} />
</View>
);
};
const UseLoader = () => {
const [visible, setVisible] = useState(true);
const showLoader = () => setVisible(true);
const hideLoader = () => setVisible(false);
const loader = visible ? <Loader /> : null;
return [loader, showLoader, hideLoader];
};
const styles = StyleSheet.create({
loadingContainer: {
backgroundColor: 'red',
flex: 1,
position: 'absolute',
...StyleSheet.absoluteFillObject,
alignItems: 'center',
justifyContent: 'center',
zIndex: 100,
padding: 10,
},
});
export default UseLoader;
And my class component is look like this :
import React, {Component} from 'react';
import {View} from 'react-native';
// import {UseLoader} from '../UseLoader';
import '../UseLoader';
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
};
}
componentDidMount() {
[loader, showLoader , hideLoader] = UseLoader;
this.callApi()
}
callApi() {
...
}
render() {
return (
<View style={styles.body}>
{loader}
</View>
);
}
}
I have tried to import functional component in both way But failed to use it.
Is any solution that can Import functional component in class component in react native ?
you can use this.
You can add a ref to the child component:
<loader ref='loader' {...this.props} />
Then call the method on the child like this:
<Button onPress={this.refs.loader.myfunc} />
Same functionality, but instead of using a String to reference the component, we store it in a global variable instead.
<loader ref={loader => {this.loader = loader}} {...this.props} />
<Button onPress={this.loader.myfunc} />
If you want to do it common, change the state on the class component, where you send if it is visible or not, like this:
const Loader = (props) => {
if(props.show){
return (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={loaderColor} />
</View>
);
}else{
return null;
}
};
and in your class component
import React, {Component} from 'react';
import {View} from 'react-native';
// import {UseLoader} from '../UseLoader';
import '../UseLoader';
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
};
}
componentDidMount() {
this.setState({showLoading:true});
this.callApi()
}
callApi() {
...
}
render() {
return (
<View style={styles.body}>
<loader show={this.state.showLoading} />
</View>
);
}
}

React native snapshot screen

I am building an app for iOS with React native and I would like to know how to take a snapshot of a given screen. I have found this library but I don't know how to use it. Does anyone know how to ?
EDIT:
I used the following code to capture a screen using the library but I get the given error.
try {
captureRef(viewRef, {
format: "jpg",
quality: 0.8
})
.then(
uri => console.log("Image saved to", uri),
error => console.error("Oops, snapshot failed", error)
);
} catch (e) {
console.log(e);
}
The error
ReferenceError: viewRef is not defined
Does anybody know how to fix the error?
Thank you
Sure, but you have to read a little about what a ref is. If you are already using React hooks, check this: https://es.reactjs.org/docs/hooks-reference.html#useref
(if not, just search on how to create a ref in React with createRef)
Basically, a ref is something that will let you identify your component using the same variable even if the component re-renders. So, viewRef in your example should be a reference to a given element. Like:
import React, { useRef } from "react";
function MyComponent() {
const viewRef = useRef();
return <View ref={viewRef}>content</View>
}
So, your draft could be something like:
import React, { useRef } from "react";
import {Button, View, Text} from 'react-native';
import { captureRef } from "react-native-view-shot";
function useCapture() {
const captureViewRef = useRef();
function onCapture() {
captureRef(captureViewRef, {
format: "jpg",
quality: 0.9
}).then(
uri => alert(uri),
error => alert("Oops, snapshot failed", error));
}
return {
captureViewRef,
onCapture
};
}
function MyComponent() {
const { captureViewRef, onCapture } = useCapture();
return (
<>
<View ref={captureViewRef}><Text>content</Text></View>
<Button title="Save" onPress={onCapture} />
</>
);
}
As far as I know, this only generates a temporary file. If you want to see the capture saved into your device, you should use CameraRoll https://facebook.github.io/react-native/docs/cameraroll
I won't cover how to use it here, but it would be something like:
CameraRoll.saveToCameraRoll(uri); // uri being the path that you get from captureRef method
Just notice that your app must ask for proper permission before attempting to save to the device gallery.
hi this can be with the help of react-native-view-shot
this is my parent component
import React, {Component,useRef} from 'react';
import {Platform, StyleSheet, Text, View,Image,Button} from 'react-native';
import { captureRef, captureScreen ,ViewShot} from "react-native-view-shot";
import NewVd from './NewVd';
import Newved from './Newved';
export default class App extends Component {
constructor(){
super();
this.state={
item:null,
captureProcessisReady:false,
myView:false
};
this.func=this.func.bind(this);
}
componentDidMount(){
}
result1=()=>{
console.log("i am here ");
this.setState({captureProcessisReady:true});
}
func = (uri) => {
console.log('ADD item quantity with id: ', uri);
this.setState({item:uri,myView:true});
};
render() {
return (
<View style={styles.container}>
{/* <NewVd
func={this.func}/> */}
<Newved />
<Text>...Something to rasterize...</Text>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>To get started, edit App.js</Text>
<Button onPress={()=>this.result1()} title="press Me"/>
<View>
{this.state.captureProcessisReady?( <NewVd func={this.func}/>):null}
</View>
<View>
{this.state.myView?( <Image source={{uri:this.state.item !== null?`${this.state.item}`:'https://picsum.photos/200'}} style={{width:100,height:100}} />):null}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
this is my child component
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
import ViewShot from "react-native-view-shot";
class NewVd extends Component {
constructor(props){
super(props);
}
onCapture = uri => {
console.log("do something with ", uri);
this.props.func(uri); //for the parent using callback
}
render() {
return (
<ViewShot onCapture={this.onCapture} captureMode="mount">
<Text>...Something to rasterize...</Text>
</ViewShot>
);
}
}
export default NewVd;

Is there an easy way to create a logout button in the drawer in React Navigation V2?

I want to have logout button in my drawer. The problem is that it should not render a screen, but just straight up log out. Is there a easy way to do it (e.g. somehow modifying the contentOptions' items or onItemPressed property? I couldn't figure something out.
What I'm doing right now is writing a CustomDrawerComponent with a logout button in it, but it's pretty hard to get the styling right and look alike the other DrawerItems.
Here is how I solved this with a custom component. Maybe there is a different way?
import React, { PureComponent } from "react";
import { Image, ScrollView, Text, TouchableOpacity } from "react-native";
import { DrawerItems, SafeAreaView } from "react-navigation";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { clearToken } from "../../api/secureStorage/secureStorage";
import { BUTTON_TEXT_LOGOUT } from "../../config/constants/buttonTexts";
import { logout } from "../../redux/actions/logout/logout";
import styles from "./styles";
export class BurgerMenu extends PureComponent {
static propTypes = {
navigation: PropTypes.object,
logout: PropTypes.func.isRequired
};
logout = () => {
const { navigation, logout } = this.props;
clearToken().then(() => {
logout();
navigation.navigate("LoginScreen");
});
};
render() {
const { logout, ...strippedProps } = this.props; // eslint-disable-line no-unused-vars
return (
<SafeAreaView style={styles.container} forceInset={{ top: "always", horizontal: "never" }}>
<ScrollView>
<DrawerItems {...strippedProps} />
</ScrollView>
<TouchableOpacity style={[styles.footer, styles.item]} onPress={this.logout}>
<Image
source={require("../../assets/icons/exit.png")}
style={styles.icon}
resizeMode="contain"
/>
<Text style={styles.text}>{BUTTON_TEXT_LOGOUT}</Text>
</TouchableOpacity>
</SafeAreaView>
);
}
}
export default connect(
null,
{ logout }
)(BurgerMenu);

React Native calling another .js inside App.js render

Is it possible to call another render() inside my App.js render. I just start working with react native, so it might look stupid.
I create the following file. Splash.js
import React, { Component } from 'react'
import { StyleSheet, Text, View } from 'react-native'
export default class Splash extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.title}></Text>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
title: {
fontWeight: 'bold',
fontSize: 18
}
})
How can I call it inside my App.js to be the default page?
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default class App extends React.Component {
render() {
return (
// Call the Splash.js
)
}
}
Thanks
There is no need to call render() inside a render() function. You can convert your splash component into a functional component, which just returns the JSX:
import React from 'react';
import {View, Text} from 'react-native';
export default function Splash() {
return (
<View>
<Text>Splash</Text>
</View>
);
}
Your app component will then render the returned JSX like so:
import React from 'react'
import Splash from './your-path-to-the-splash-file'
export default class App extends React.Component {
render() {
return (
<View>
<Splash/>
</View>
);
}
};
You should check out the official react docs: https://reactjs.org/docs/components-and-props.html

React Native Router Flux not Showing Screen

I've a view which I'm trying to load via the react-native-router-flux module.
However, it is not showing the screen on emulator. However, I can see my Components in the react-dev tools.
I don't see any error but an Empty screen on Android Emulator. Details follow:
Test.js :
import React from 'react';
import { Text, View } from 'react-native';
const Test = () => {
return (
<View style={{margin: 128}}>
<Text>This is PageTwo!</Text>
</View>
);
};
export default Test;
My Router: Router.js
import React, { Component } from 'react';
import { Router, Scene } from 'react-native-router-flux';
import LoginForm from './components/LoginForm';
import Test from './components/Test';
class RouterComponent extends Component {
render() {
return (
<Router>
<Scene key="root" >
<Scene key="pageOne" component={Test} title="PageOne" initial={true} />
<Scene key="pageTwo" component={LoginForm} title="PageOne" initial={false} />
</Scene>
</Router>
);
}
}
export default RouterComponent;
My App Loader:
import React, { Component } from 'react';
import { View } from 'react-native';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import firebase from 'firebase';
import ReduxThunk from 'redux-thunk';
import reducers from './reducers';
import RouterComponent from './Router';
import LoginForm from './components/LoginForm';
class App extends Component {
componentWillMount() {
// Initialize Firebase
}
render() {
return (
<Provider store={createStore(reducers, {}, applyMiddleware(ReduxThunk))}>
<View>
<RouterComponent />
</View>
</Provider>
);
}
}
export default App;
Android Emulator Screen:
React dev tools:
Package.json:
Please help.
I don't think is the stateless component's issue, I added a flexbox styling to the <View> component that wraps around the <RouterComponent> and it works on my Android emulator, simply removing the <View> wrapper around the <RouterComponent> would also work:
class App extends Component {
render() {
return (
<Provider store={createStore(reducers, {}, applyMiddleware(ReduxThunk))}>
<View style={{ flex: 1 }}>
<RouterComponent />
</View>
</Provider>
);
}
}
Many time in app.js we used
container: {
backgroundColor: '#455e64',
flex : 1,
alignItems: 'center',
justifyContent: 'center',
}
alignItems: 'center',
so we can get the same error
we need to just remove alignItems:'center', from style it will fix your problems.
container: {
backgroundColor: '#455e64',
flex : 1,
justifyContent: 'center',
}
This is insane. Spent 2 hours debugging. Figured out that component should not be stateless, you have to define it as a class that extends Component.
So in your Test.js instead of:
import React from 'react';
import { Text, View } from 'react-native';
const Test = () => {
return (
<View style={{margin: 128}}>
<Text>This is PageTwo!</Text>
</View>
);
};
export default Test;
just do:
import React, { Component } from 'react';
import { Text, View } from 'react-native';
class Test extends Component {
render() {
return (
<View style={{margin: 128}}>
<Text>This is PageTwo!</Text>
</View>
);
}
};
export default Test;
Just added a flexbox styling to the View for wrap component RouterComponent. And the idea is work also in my Android emulator.
Or you can remove component View like :
class App extends Component {
componentWillMount() {
// Initialize Firebase
}
render() {
return (
<Provider store={createStore(reducers, {}, applyMiddleware(ReduxThunk))}>
<RouterComponent />
</Provider>
);
}
}
I hope the idea can helping you.