Getting apollo client in sub view to use queries manualy - react-native

I'm pretty new using react-native and I don't get something concerning the usage of ApolloClient in my app. I can't understand how to use the client to make request to my graphql api. This is my App.js file.
import React from 'react';
import { ApolloProvider, Query } from 'react-apollo';
import Client from './src/Network/client';
import AppContainer from './src/Navigation/navigator';
export default class App extends React.Component {
constructor(props) {
super(props);
this.client = new Client().createClient();
}
render() {
return (
<ApolloProvider client = {this.client}>
<AppContainer/>
</ApolloProvider>
);
}
}
So, my AppContainer is a basic stack navigator. I've read that I sould use the component Query from react-apollo, but I wanted to have more "control" on my queries and use them manualy if possible. I would like to have something like a function in the subviews of my navigator which could do :
function getUserInfo() {
client.query({MyGraphQuery})... ;
}
How to perform this trick ?
Thanks in advance

I'm not 100% positive. I've never done this myself. But I believe you'd have to do it this way...
function async getUserInfo() {
return await client.query(MyGraphQuery);
}
I made the function async
returned the query result after awaiting
remove the {} around the gql tagged query definition. (Make sure it is a gql tagged query). I'm not positive on this syntax though.
Hope this helps.

Related

The action POP with payload was not handled by any navigator

I have no idea what is causing this bug in my react native app. I'm using version 5 of the React Navigation library.
It randomly crashes the app sometimes. Google searching hasn't helped me understand what this is. It's very selective though which is a good(or a bad) thing.
So what does this mean and what could be causing it?
if(navigation.canGoBack()) {
navigation.dispatch(StackActions.pop(1));
}
see https://github.com/react-navigation/react-navigation/issues/7814#issuecomment-599921016
Did you try this?
this.props.navigation.goBack()
It means you tried to pop a view where there was nothing to pop. It might mean there's a bug in your app because, generally, you shouldn't be popping a view when there isn't any to pop.
But it can also be part of intentional design where you have insufficient knowledge of the current navigation state, but need to make sure at least one pop is done (similar to clearing a flag variable even if it might not be set in the first place, in which case it would be a no-op). If that's the case, then you can disable this development-level warning:
const temp = console.error;
console.error = () => {};
navigation.pop();
console.error = temp;
Error Cause: goBack() or pop() is getting called multiple times. Sometimes onPress event gets called many times. You can check by adding console.log().
How to Solve: you need to throttle the onPress function.
Example:
import React, { PureComponent } from 'react'
import { Text, View } from 'react-native'
import { throttle } from 'lodash'
export default class Test extends PureComponent {
constructor(props) {
super(props)
this.state = {
}
this.onPress = throttle(this.onPress, 500, {trailing: false})
}
onPress = () => {
console.log("going back")
this.props.navigation.pop();
//this.props.navigation.goBack();
}
render() {
return (
<View>
<Text>Hello World!</Text>
</View>
)
}
}
you need to check there can go back or not by canGoBack method like this
import { StackActions } from '#react-navigation/native';
if(this.refs.navigation.canGoBack())
{
this.refs.navigation.dispatch(StackActions.pop(1));
// this.refs.navigation.dispatch(StackActions.popToTop());
}

Can log updated value but cant render it with mobx flow

I am trying to make a really simple api call without any logic at all.Althoough I get an illegible object in the consoel called 'proxy' at leaset (not expected either) I cant return anything in the render() method and it throws a typeError.
my code:
Store:
import {observable, configure, action,flow, computed, decorate, set, runInAction} from 'mobx';
import {observer, inject} from 'mobx-react'
configure({enforceActions:'observed'})
class GenStore {
verseData = []
state = "pending"
getVerseData = flow(function*() {
this.verseData = []
this.state = "pending"
try {
const response = yield fetch('https://api.quranwbw.com/2/10')
const data = response.json()
this.state = "done"
this.verseData = data
} catch (error) {
this.state = "error"
}
})
}
decorate(GenStore, {state:observable, verseData: observable, getVerseData:action})
export default new GenStore()
Retrieval:
import {observable, configure, action,flow, computed, decorate, set, runInAction} from 'mobx';
import { computedFn } from "mobx-utils"
import {observer, inject} from 'mobx-react'
import React from 'react'
import GenStore from './GenStore'
class Show extends React.Component{
componentDidMount(){
this.props.GenStore.getVerseData()
}
render(){
console.log(this.props.GenStore.verseData)
return <h1>{this.props.GenStore.verseData.words[0].word_arabic}</h1>
}
}
export default inject('GenStore')(observer(Show))
error returned when i try to render:
TypeError: Cannot read property '0' of undefined
Any help would be appreciated.
Thanx in advance.
Oh, and if you have any suggestion as to how to implement this call if you think flow isnt the choice method, please advise me and tell me how i can do it best
Because getVerseData is async function and when component renders for the first time verseData is an empty array (why it is empty array though? It should be empty object) and respectively verseData.words is undefined.
You can do several things to deal with it, for example, check if verseData.words exists and if not show some loader component instead.

Changing state in React native App.js from another component

I'm making authentication in an app, and I'm kind of stuck. I have 2 different navigations. One shows if the user is logged in and another one if not. Basically, a Sign in screen. It's working fine if I change the value manually upon the start. But I can't find a way to change a state when a user signs in, for example. Even though the value in auth module changes, it doesn't update in App.js So how can I update the App.js's state from Sign in screen, for example?
import React, { Component } from 'react';
import { AppRegistry, Platform, StyleSheet, Text, View } from 'react-native';
import DrawerNavigator from './components/DrawerNavigator'
import SignedOutNavigator from './components/SignedOutNavigator'
import auth from './auth'
type Props = {};
export default class App extends Component<Props> {
constructor(props) {
super(props)
this.state = {
isLoggedIn: auth.isLoggedIn
}
}
render() {
return (
(this.state.isLoggedIn) ? <DrawerNavigator /> : <SignedOutNavigator />
);
}
}
AppRegistry.registerComponent('App', () => App)
and my auth module, which is very simple
import { AsyncStorage } from 'react-native';
// try to read from a local file
let api_key
let isLoggedIn = false
function save_user_settings(settings) {
AsyncStorage.mergeItem('user', JSON.stringify(settings), () => {
AsyncStorage.getItem('user', (err, result) => {
isLoggedIn = result.isLoggedIn
api_key = result.api_key
});
isLoggedIn = true
});
}
module.exports.save_user_settings = save_user_settings
module.exports.api_key = api_key
module.exports.isLoggedIn = isLoggedIn
First off, there are loads of ways to approach this problem. Because of this I'm going to try explain to you why what you have now isn't working.
The reason this is happening is because when you assign auth.isLoggedIn to your isLoggedIn state, you are assigning the value once, kind of as a copy. It's not a reference that is stored.
In addition to this, remember, React state is generally only updated with setState(), and that is never being called here, so your state will not update.
The way I would approach this problem without bringing in elements like Redux, which is overkill for this problem by itself, is to look into building an authentication higher order component which handles all the authentication logic and wraps your entire application. From there you can control if you should render the children, or do a redirect.
Auth Component
componentDidMount() {
this._saveUserSettings(settings);
}
_saveUserSettings(settings) {
AsyncStorage.mergeItem('user', JSON.stringify(settings), () => {
AsyncStorage.getItem('user', (err, result) => {
isLoggedIn = result.isLoggedIn
api_key = result.api_key
});
this.setState({isLoggedIn: true});
});
}
render() {
const { isLoggedIn } = this.state;
return isLoggedIn ? this.props.children : null;
}
App.js
render() {
<AuthComponent>
//the rest of authenticated app goes here
</AuthComponent>
}
Here's a really quick, incomplete example. But it should showcase to you how you may want to lay your authentication out. You'll also want to consider error handling and such, however.

React Native with Redux: mapStateToProps()-function is not updating

I am working at a React Native and Redux project with several reducers, that are combined through combineReducers().
I have one component, that doesn't dispatches any actions but only shows information from the redux state. It is connected to this state with the connect function:
class MyComponent extends React.Component {
componentWillMount() {
// some code
}
render() {
return(
<Text>
{ this.props.value }
</Text>
)
}
}
function mapStateToProps(state) {
return {
value: state.updaterReducer.value,
}
}
export default connect(
mapStateToProps,
) (MyComponent);
There's another part/component in the project, that changes the redux state and value:
import { makeUpdate } from './updater.actions.js';
import configureStore from '#redux/configureStore.js';
const store = configureStore();
// This function is can be called by some buttons in the app:
export default function update() {
console.log('Update function called');
store.dispatch(makeUpdate());
}
And here's my problem: when update is called, the updater-action and the updater-reducer are both called, so that the redux state changes, but 'MyComponent' never updates.
I could solve the problem on my own: The solution was very easy in the end, but couldn't be found on the basis of the code in the original question: Every time I needed the redux-store, I used a configureStore-function from a web-tutorial to create it on basis of the reducers. So I created multiple times the 'same' store. Unfortunately these stores were not connected to each other...
Sometimes that worked in the project, because a mapStateToProps-function and a mapDispatchToProps-function both were in the the same component and used one store, but sometimes (like in the example in the question) those functions used different stores and couldn't influence each other.

Redirect to login page when not logged in causes state transition error

I am struggling to figure out how to correctly redirect to a login page when the user is not logged in using React and Redux.
Currently, in the constructor of the component, I check to see if the username is set, and if not, I use the routeActions provided by redux-simple-router to redirect to the login page. However, I get this error:
Warning: setState(...): Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
I understand that setting the state inside of the render function should be avoided. but I am not sure where I should detect and redirect. I have also tried checking the auth state in the componentWillReceiveProps and ComponentWillMount, but no luck.
// WordListContainer.js
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {routeActions} from 'redux-simple-router';
import WordList from '../components/Words/WordList';
import {addWord, editWord, deleteWord, fetchWords} from '../actions/words';
function mapStateToProps(state) {
return {
auth: state.auth,
words: state.words
};
}
function mapDispatchToProps(dispatch) {
return {
router: bindActionCreators(routeActions, dispatch),
actions: bindActionCreators({
addWord, editWord, deleteWord, fetchWords
}, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(WordList);
and
// WordList.js
import React, {Component} from 'react';
import {Link} from 'react-router';
import WordListItem from './WordListItem';
export default class WordList extends Component {
constructor(props) {
super(props);
if(!this.props.auth.username) {
// This redirection causes the error
this.props.router.push('/login');
}
}
render() {
...
}
}
Is there a good place where I can check the state and redirect before even trying to render the component? Perhaps somehow using the Container Object, though I am not quite sure how to do it where I have access to both state and dispatch.
try componentDidUpdate() as this lifecycle method will always be called whenever the state changes.