why redux-offline with react-native not send the POST request? - react-native

I have delete_task action that POST the task id to PHP file to delete the task , although the action I fired correctly and i see the effect in the console but the task not deleted, i tried many many times using GET OR POST OR JUST put the id statically in the URL but nothing changing,can anyone help me to solve the problem
tasksAction.js
export const delete_task = id => ({
type: DELETE_TASK,
payload: {id},
meta: {
offline: {
// the network action to execute:
effect: { url: 'http://localhost/todos/remove.php', method: 'POST',body: {id} },
// action to dispatch when effect succeeds:
commit: { type: DELETE_SUCCESS, meta: { id } },
// action to dispatch if network action fails permanently:
rollback: { type: DELETE_FAILED, meta: { id } }
}
}
});
store.js
import React, { Component } from 'react';
import { applyMiddleware, createStore, compose } from 'redux';
import { offline } from '#redux-offline/redux-offline';
import offlineConfig from '#redux-offline/redux-offline/lib/defaults';
import {Provider} from 'react-redux';
import logger from 'redux-logger';
import RootNavigator from "./config/routes";
import reducers from './reducers'
// store
const middleware = [];
if(process.env.NODE_ENV === 'development'){
middleware.push(logger);
}
const store = createStore(
reducers,
undefined,
compose(
applyMiddleware(...middleware),
offline(offlineConfig)
)
);
export default class App extends Component {
render() {
return (
<Provider store={store}>
<RootNavigator />
</Provider>
)
}
}

In the offline object inside prev state, does it say online: false?
If it does, and you're developing in an Android emulator, Wifi is not available on the emulator if you are using below API level 25, so redux-offline thinks you have no connection and will not make the initial network request.
This occurred to me after testing redux-offline in an iOS simulator and everything worked.
Hope this helps.

Related

Exception in HostFunction: <unknown> when trying to push new screen

Issue Description
Trying to push a new screen with
Navigation.push(this, {
component: {
name: 'awesome-places.AuthScreen' }})
And getting an error Exepction in HostFunction <unknown>
Error Screenshot
Steps to Reproduce / Code Snippets / Screenshots
Create RN app, install required modules (redux, react-redux, react-native-navigation, react-vector-icons) and run code on Android device.
Add relevant code, App.js and component that causes the error. I tried running Navigation.push with this.props.componentId but when I press the button there is no response
App.js
import { Navigation } from 'react-native-navigation';
import { Provider } from 'react-redux';
import AuthScreen from './src/screens/Auth/Auth';
import SharePlaceScreen from './src/screens/SharePlace/SharePlace';
import FindPlaceScreen from './src/screens/FindPlace/FindPlace';
import PlaceDetailScreen from './src/screens/PlaceDetail/PlaceDetail';
import configureStore from './src/store/configureStore';
const store = configureStore();
// Register Screens
Navigation.registerComponentWithRedux("awesome-places.AuthScreen", () => AuthScreen, Provider, store);
Navigation.registerComponentWithRedux("awesome-places.SharePlaceScreen", () => SharePlaceScreen, Provider, store);
Navigation.registerComponentWithRedux("awesome-places.FindPlaceScreen", () => FindPlaceScreen, Provider, store);
Navigation.registerComponent("awesome-places.PlaceDetailScreen", () => PlaceDetailScreen);
// Start an App
Navigation.events().registerAppLaunchedListener(() => {
Navigation.setRoot({
root: {
component: {
name: 'awesome-places.AuthScreen'
}
}
});
});
FindPlace.js
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { connect } from 'react-redux';
import { Navigation } from 'react-native-navigation';
import PlaceList from '../../components/PlaceList/PlaceList';
class FindPlaceScreen extends Component {
Navigation.push(this, {
component: {
name: 'awesome-places.AuthScreen',
}
})
}
render () {
return(
<View>
<PlaceList places={this.props.places} onItemSelected={this.itemSelectedHandler} />
</View>
);
}
}
const mapStateToProps = state => {
return {
places: state.places.places
}
}
export default connect(mapStateToProps)(FindPlaceScreen);
Environment
React Native Navigation version: 2.17.0
React Native version: 0.59.4
Platform(s) (iOS, Android, or both?): Android
Device info (Simulator/Device? OS version? Debug/Release?): Android 7.1.2, Debug
I noticed you're pushing the screen with this instead of an explicit componentId.
When calling Navigation.push, the first argument needs to be a componentId. See the docs for reference.
Also, this might not be a problem, but registerComponentWithRedux is deprecated and you should register the screen with the new api

Passing initial state from API call to createStore when react native application boots up

I am having trouble initialising my redux-state when my react-native application boots up. I need to make an api call before the application boots up to retrieve data to hydrate my state. Id like to pass the result of this call to the createStore function in my Provider JSX element.
I have read different things about how to do that but none of them seems to work.
Here's my root App component :
import React, { Component } from 'react';
import { View } from 'react-native';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import ReduxThunk from 'redux-thunk';
import reducers from './reducers';
import RouterComponent from './Router';
class App extends Component {
render() {
return (
<Provider store={createStore(reducers, {}, applyMiddleware(ReduxThunk))}>
<View style={{ flex: 1 }}>
<RouterComponent />
</View>
</Provider>
);
}
}
export default App;
I have read and tried different strategies :
- wrapping the return statement of the render method in the then callback of the api call
- make the call in componentWillMount or componentDidMount
None of this did work for me. What is the standard way to pass createStore an initial state from an API call when react-native application boots up.
You cannot (and should not) delay the mounting of components until the API call returns (it might even fail).
You can show a loading screen while waiting for the API call to return, by checking if a certain Redux state (that will be populated by the API result) is still empty in your component (conditional rendering).
If you want to replace the entire Redux state with your API result, you need to write a root reducer, see the answer here.
To initiate the API call when app starts and populate the state when it succeeds, you can add the following to anywhere that the Redux store is defined/imported:
fetch(...).then(result => store.dispatch(...))
Instead of populating the Redux state from server, you can look into persisting it with the client if it suits your use case.
I would go about this by setting a loading value in the state and then requesting the data in ComponentDidMount(). Once loaded, set this.state.loaded to true and render the store with the data returned from the API. It isn't necessary to do this but it would provide a good UX for the client and prevent unnecessarily re-rendering the RouterComponent twice.
Whether or not you decide to set error and loaded values, the idea here is to get the data in the ComponentDidMount method and update App.state with the new data - this will cause the component to re-render and apply your data to the new Store.
import React, { Component } from 'react';
import { View } from 'react-native';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import ReduxThunk from 'redux-thunk';
import reducers from './reducers';
import RouterComponent from './Router';
class App extends Component {
constructor(props) {
super(props);
this.state = {
initialState: {},
loaded: false,
error: false
}
}
componentDidMount() {
// Perform your API call, using which ever library or method you choose, i prefer axios so will demonstrate with this:
axios.get('path/to/api')
.then(res => {
// Send the response to state, which will cause the component to re-render and create the store with the new initialState
this.setState({
initialState: res.data,
loaded: true
});
})
.catch(err => {
console.error('Error initiating application. Failed to retrieve data from API')
this.setState({error: true});
});
}
render() {
// This would be completely optional, but I would show some form of loading icon or text whilst you wait for the API to fetch the data.
if(!this.state.loaded) {
return "Loading";
}
// If there was an error getting the data, tell the client
else if(this.state.error) {
return "Error loading data from API. Please reload the application or try again.";
}
// If all is well, the component should render the store with the correct initialState
else {
return (
<Provider store={createStore(reducers, this.state.initialState, applyMiddleware(ReduxThunk))}>
<View style={{ flex: 1 }}>
<RouterComponent />
</View>
</Provider>
);
}
}
}
export default App;
I hope this helps.
Its better to use server-rendering.
counterApp.js
export const counterApp = (state = {}, action) => {
switch (action.type) {
default:
return state;
}
}
server.js
//this route will get called for initial page load or page refresh.
server.get('/', (req, res) => {
const counter = parseInt(20, 10) || 0
// demo state,It can be function calling database operation or API.
let preloadedState = { counter } ;
const store = createStore(counterApp, preloadedState);
const html = renderToString(
<Provider store={store}>
<App />
</Provider>
);
const finalState = store.getState()
res.send(renderFullPage(html, finalState))
});
RenderFullPage :
function renderFullPage(html, preloadedState) {
return `
<!doctype html>
<html>
<head>
<title>Redux Universal Example</title>
</head>
<body>
<div id="root">${html}</div>
<script>
// WARNING: See the following for security issues around embedding JSON in HTML:
// http://redux.js.org/recipes/ServerRendering.html#security-considerations
window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\u003c')}
</script>
<script src="/static/bundle.js"></script>
</body>
</html>
`
}
In App.js,( going to be render on client side only.)
In App.js,you can access initial state using window.__APP_INITIAL_STATE__,
render(<App {...window.__APP_INITIAL_STATE__} />, document.getElementById('root'));
For server side rendering,you have to setup webpack or else you prefer.

Cannot read property 'getItem' of undefined React Native and Parse

I've started learning React Native with Redux and I'm building a simple login with Google and Facebook login which I want to sent to a Parse backend on AWS.
I have the following Facebook Button Component:
import { Button } from 'react-native';
import { connect } from 'react-redux';
import React, { Component } from 'react';
import { LoginManager } from 'react-native-fbsdk';
import { createUser } from '../actions';
class FacebookButton extends Component {
facebookButtonPressed(){
this.props.createUser("dummyemail#gmail.com","12345");
}
render() {
return (
<Button
onPress={this.facebookButtonPressed.bind(this)}
title="Sign In with Facebook"
color="#000000"
/>
);
}
}
export default connect(null,{ createUser })(FacebookButton);
I haven't done any Facebook integration yet, I just want to see if I can create a parse user. This is the action I'm using:
import Parse from 'parse/react-native';
export const createUser = (email, name) => {
return (dispatch) => {
var user = new Parse.User();
user.set("username", "my name");
user.set("password", "my pass");
user.set("name",name);
user.set("email", email);
// other fields can be set just like with Parse.Object
user.set("phone", "415-392-0202");
user.signUp(null, {
success: function(user) {
// Hooray! Let them use the app now.
console.log("User created");
},
error: function(user, error) {
// Show the error message somewhere and let the user try again.
alert("Error: " + error.code + " " + error.message);
}
});
}
};
When I run the debugger in the browser, I'm able to see that the createUser is called and when we call user.signUp I get the following error:
undefined is not an object (evaluating 'reactNative.AsyncStorage.getItem')
When I read the Parse Javascript docs and try the following:
//Get your favorite AsyncStorage handler with import (ES6) or require
import { AsyncStorage } from 'react-native';
//Before using the SDK...
Parse.setAsyncStorage(AsyncStorage);
I call the Parse.setAsyncStorage(AsyncStorage); in the code below.
import ReduxThunk from 'redux-thunk';
import reducers from './src/reducers';
import Parse from 'parse/react-native';
import { Provider } from 'react-redux';
import Login from './src/screens/Login';
import React, { Component } from 'react';
import { AsyncStorage } from 'react-native';
import { createStore, applyMiddleware } from 'redux';
export default class App extends Component {
componentWillMount() {
Parse.initialize("MY KEY");
Parse.serverURL = 'MY URL';
Parse.setAsyncStorage(AsyncStorage);
}
render() {
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
return (
<Provider store={store}>
<Login />
</Provider>
);
}
}
I get an error of reactNative2.default.setAsyncStorage is not a function. Frankly I tried Parse.setAsyncStorage(AsyncStorage); in hopes of this fixing the AsyncStorage issue mentioned above.
So any ideas as to why I get the funny AsyncStorage error message when trying to create a Parse user?
I'm leaving an answer here in case someone else stumbles across any other issues. The problem was the version of Parse I was using. I was using Parse version 1.6.14. Steps to fix:
Open the package.json file and update your Parse version to any version above 1.11, for me 1.11.1 worked.
In the terminal: npm install
this should fix the issues mentioned above, bear in mind that you have to implement:
//Get your favorite AsyncStorage handler with import (ES6) or require
import { AsyncStorage } from 'react-native';
//Before using the SDK...
Parse.setAsyncStorage(AsyncStorage);
as per documentation.

Apollo with redux-persist

I integrated react-apollo for fetching data from my GraphQL server into my react native application. I use redux-perist for persisting and rehydrating the redux store.
But I don't know how to rehydrate my apollo state.
Anyone know how I can do that?
Thanks
You can use the autoRehydrate feature of redux-persist. It will asyncronously run an action with type REHYDRATE that will rehydrate all stores unless you blacklist them.
If you try to execute a query that was previously executed, Apollo will check your Redux store first and you should see an APOLLO_QUERY_RESULT_CLIENT action execute (meaning it's returning from the client store instead of querying the server). You can modify the fetchPolicy to specify how the query gets its data (network only, cache first, etc.)
Here's a basic setup:
import React, { Component } from 'react';
import { ApolloProvider } from 'react-apollo';
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import ApolloClient, { createNetworkInterface } from 'apollo-client';
import { persistStore, autoRehydrate } from 'redux-persist';
import { AsyncStorage } from 'react-native';
const networkInterface = createNetworkInterface({ uri: 'http://localhost:8080/graphql' });
export const client = new ApolloClient({
networkInterface: networkInterface,
});
const store = createStore(
combineReducers({
apollo: client.reducer(),
}),
{}, // initial state
compose(
applyMiddleware(client.middleware()),
autoRehydrate(),
),
);
// persistent storage
persistStore(store, {
storage: AsyncStorage, // or whatever storage you want
});
export default class App extends Component {
render() {
return (
<ApolloProvider store={store} client={client}>
<YourApp />
</ApolloProvider>
);
}
}

Actions may not have an undefined "type" property - React-Native/Redux

I am receiving the Actions may not have an undefined "type" property. Have you misspelled a constant? error from all actions within the RecommendationItemActions.js actions file.
This issue occurs with both exports contained within.
import firebase from 'firebase';
import {} from 'redux-thunk';
import {
RECOMMENDATION_ITEM_UPDATE,
RECOMMENDATION_ITEMS_FETCH_SUCCESS
} from './types';
export const recommendationItemUpdate = ({ prop, value }) => {
//ex. Want to change name? Send in prop [name] and value [ what you want the new name to be ]
return {
type: RECOMMENDATION_ITEM_UPDATE,
payload: { prop, value }
};
};
export const recommendationItemsFetch = () => {
const { currentUser } = firebase.auth();
return (dispatch) => {
firebase.database().ref(`/users/${currentUser.uid}/recommendationItems`)
//snapshot is the value found
//snapshot is an object that describes what data is there
.on('value', snapshot => {
console.log(snapshot.val());
dispatch({
type: RECOMMENDATION_ITEMS_FETCH_SUCCESS,
payload: snapshot.val()
});
//snapshot.val() is the actual data at snapshot
});
};
};
These types are clearly defined within the imported type.js file
export const RECOMMENDATION_ITEMS_FETCH_SUCCESS = 'recommendation_items_fetch_success';
export const RECOMMENDATION_ITEM_UPDATE = 'recommendation_item_update';
The Actions are exported via the index.js file in the actions folder
export * from './RecommendationItemActions';
They are imported within the RecommendationItemCreate.js file
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Text, Image, View } from 'react-native';
import { recommendationItemUpdate, recommendationItemCreate } from '../../actions';
import { CardSection, Input, Button } from './../common';
import GooglePlacesAutocomplete from './../googlePlaces/GooglePlacesAutocomplete';
And referenced within the onChangeText of my Input component
<Input
label="ADDRESS"
placeholder="Address"
value={this.props.formattedAddress}
onChangeText={
value => this.props.recommendationItemUpdate({ prop: 'formattedAddress', value })
}
/>
I can not trace any error that would lead to the Actions may not have an undefined "type" property and have been banging my head against this for a couple days now. The type is clearly defined and can be accurately traced throughout it's usage.
Closest Reference Found
React+Redux - Uncaught Error: Actions may not have an undefined “type” property
This was still unhelpful. Here is my createStore as reference:
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import firebase from 'firebase';
import ReduxThunk from 'redux-thunk';
import reducers from './reducers';
import Router from './Router';
class App extends Component {
componentWillMount() {
const config = {
REMOVED FOR EXAMPLE
};
firebase.initializeApp(config);
}
render() {
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
//applyMiddleware is a store-enhancer - it adds additional functionality
//to store
return (
<Provider store={store}>
<Router />
</Provider>
);
}
}
export default App;
I believe I have hit an issue like this before, and it was caused by dispatching a plain object within a thunk rather than an action creator, such as what you are doing. Could you try creating a recommendationItemsFetchSuccess action creator and dispatching that? Such as:
const recommendationItemsFetchSuccess = (snapshotVal) => {
return {
type: RECOMMENDATION_ITEMS_FETCH_SUCCESS,
payload: snapshotVal
};
}
export const recommendationItemsFetch = () => {
const { currentUser } = firebase.auth();
return (dispatch) => {
firebase.database().ref(`/users/${currentUser.uid}/recommendationItems`)
.on('value', snapshot => {
dispatch(recommendationItemsFetchSuccess(snapshot.val()));
});
};
};
As stated by DJSrA the fix is a reference error. In my case, I was importing from two different files that contains constants. I referenced the wrong file for a particular constant.