AsyncStorage to save state with React Native - firebase

I want to save the switch button state so users can't vote twice.
I have a message error : Exception '-[NSDictionaryM length...was thrown while invoking multiSet on target AsyncLocalStorage Any Idea ?
this.state= {
trueSwitchIsOn: false,
};
onSwitchChange(_key){
const{trueSwitchIsOn}=this.state;
switch (this.state.trueSwitchIsOn){
case false:
return(
<TouchableHighlight onClick={this.onPressIcon(_key)}>
{this.setState({trueSwitchIsOn: true})}
</TouchableHighlight>
);
case true:
return(
<TouchableHighlight onClick={this.onUnPressIcon(_key)}>
{this.setState({trueSwitchIsOn: false})}
</TouchableHighlight>
);
}
}
onPressIcon(word){
AsyncStorage.setItem('AlreadyLiked', {trueSwitchIsOn});
const{trueSwitchIsOn}=this.state;
this.setState({trueSwitchIsOn : true});
}
onUnPressIcon(word){
AsyncStorage.setItem('NotAlreadyLiked', {trueSwitchIsOn: false});
const{trueSwitchIsOn}=this.state;
this.setState({trueSwitchIsOn : false});
<Switch>
onValueChange={(value)=>this.onSwitchChange(_key)}
</Switch>

The value passed to AsyncStorage.setItem() needs to be a string. You need to either pass your object to JSON.stringify() or just use a pure string value instead of an object with a "trueSwitchIsOn" boolean property.

Related

Component definition is missing display nameeslintreact/display-name

I have the following Component in my NextJs app
const Input = React.forwardRef((props, ref) => <input {...props} />)
All I keep getting as error is
Component definition is missing display nameeslintreact/display-name
What is possibly the reason?
You are using anonymous arrow function to create the component. If you use a regular function you won't get that error.
const Input = React.forwardRef(function Input(props, ref) { return <input {...props} />})
Or you can just add displayName to Input const
Input.displayName = 'Input'

react native navigate to a page with some parameter based on firebase dynamic link (probably just react native link should work the same way)

I am building an app with react native and I used firebase dynamic link to open the app. it works well but I want the user to navigate to a specific screen with some parameter I want to pass along when the app opens by responding to link.
Below is how my app.js looks like. this code is getting triggered every time I am clicking the link. now I want to navigate to some specific screen that will accept some parameters. e.g. navigation.navigate('Profile',{param1:'xyz', param2:'sdfds'}) but i am not sure how i can make it work. I could put this dynamic link handler in my initial route (Main screen as per below), but I tried it once and it did not quite work. I will try again but was wondering if there are some pointers?
import dynamicLinks from '#react-native-firebase/dynamic-links';
export default function App() {
const Stack = createStackNavigator();
const handleDynamicLink = link => {
dynamicLinks()
.getInitialLink()
.then(link => {
console.log(JSON.stringify(link) + 'i got triggered and app was open');
if (link.url === 'https://myapp.page.link/welcome') {
console.log('i got triggered and app was open');
//Stack.Navigator.initialRouteName = MenuScreen;
//navigation.navigate('Profile');
// ...set initial route as offers screen
}
});
// Handle dynamic link inside your own application
if (link.url === 'https://myapp.page.link/welcome') {
// ...navigate to your offers screen
console.log('i got triggered and app was closed');
//navigation.navigate('Profile');
}
};
useEffect(() => {
const unsubscribe = dynamicLinks().onLink(handleDynamicLink);
// When the is component unmounted, remove the listener
return () => unsubscribe();
}, []);
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: 'orange',
},
headerTintColor: 'black',
headerTitleStyle: {
fontWeight: 'bold',
},
}}>
<Stack.Screen name="Menu" component={HomeScreen} />
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Store" component={StoreScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}

Reading and displaying data from a Firestore Document in React Native

Background
I'm trying to build an app which shows a number of stores, in the home screen which is a function component (mind this as I need to use hooks) I have a scroll view which shows different stores.
What I need
When the user presses on one of the stores it should redirect it to a screen which has the information of that specific store. I have built the "store detail" screen but with static info, I want to replace all of that information with data stored in a firestore collection.
Question
How would one go about retrieving data from a Firestore collection in react native, then assigning the data from each document to a separate Touchable Opacity (I know about passing params with react navigation, I just don't know which param to pass when working with Firestore), and then displaying that data in the store detail screen?
Sample code for context
App.js
<NavigationContainer>
<Stack.Navigator initialRouteName={user ? 'Home' : 'Login'}
screenOptions={{cardStyle: { backgroundColor: '#FFFFFF' }}}>
<Stack.Screen name="Home"options={{headerShown: false}}>
{props => <HomeScreen {...props} extraData={user} />}
</Stack.Screen>
<Stack.Screen name="Login" component={LoginScreen} options={{headerShown: false}}/>
<Stack.Screen name="Registration" component={RegistrationScreen} options={{headerShown: false}}/>
<Stack.Screen name="storeDetail" options={{title: ''}}>
{props => <storeDetail {...props} extraData={} />}
</Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
In this file you'll see that I've already called some data (Login and Register pass userData to the Home Screen), however in order to implement that method I depended on the response from the authentication method I was using. I imagine although, I will probably need to pass something as extraData, I understand what I should do, I just don't know how to fill the blank spaces.
Thanks a lot in advance!
First, install the Firebase SDK in your app, so you can make queries to your backend.
I don't know if your sample App.js represents the current state of progress on your app, but I'm going to assume that:
you already have your storeDetail screen built
you know the store's id before navigating to the screen (eg in the HomeScreen)
you pass the storeId as a navigation param when navigating to storeDetail
So in storeSetails screen, you can query Firestore when receiving storeId, and save the result to a state variable on success:
const StoreDetailsScreen = ({ route }) => { // route is passed as a prop by React Navigation
const { storeId } = route.params
const [store, setStore] = useState()
const [loading, setLoading] = useState(true) // show a loading spinner instead of store data until it's available
useEffect(() => {
const fetchQuery = async () => {
const storeData = await firestore()
.collection('stores')
.doc(storeId)
.get() // this queries the database
if (storeData) {
setStore(storeData) // save the data to store state
setLoading(false) // set loading to false
} else {
// something went wrong, show an error message or something
}
}
fetchQuery()
}, [storeId])
if (loading) {
return (
<ActivityIndicator/>
)
}
return (
// ... store details
)
}
Then you can use the data in store to render stuff in your screen
<Text>{store.name}</Text>
<Text>{store.email}</Text>
// ...
More info about how to use Firestore in RN: https://rnfirebase.io/firestore/usage

My redux wizard form doesn't carry over form values

so I have a 4 page form, the first 3 have form fileds while the last one is just a success page shown after the data is submitted.
I have initial values set for fields on each page in my quoteReducer like so:
import {
QUOTE_SUBMIT,
GET_QUOTES,
SET_QUOTE_INITIAL_VALUES
} from "../actions/types";
const initialQuoteForm = {
your_details: {
title: "Mr",
employment_status: "Employed"
},
your_house: {
alarm_type: "Standard",
two_or_more_smoke_alarms: false,
neighbourhood_watch: false,
property_type: "Bungalow",
cover_type: "Holiday Home",
heating_type: "Electric"
},
your_insurance_details: {
first_time_buyer: false,
currently_or_previously_insured: false,
landlord_or_tennant: "Landlord"
}
};
const initialState = {
quotes: [],
currentQuote: {},
quoteInitial: initialQuoteForm,
message: "",
ok: true,
error: []
};
export default function (state = initialState, action) {
switch (action.type) {
case GET_QUOTES:
return {
...state,
ok: action.payload.ok,
message: action.payload.message,
errors: action.payload.errors,
quotes: action.payload.quotes
};
case QUOTE_SUBMIT:
return {
...state,
ok: action.payload.ok,
message: action.payload.message,
errors: action.payload.errors,
currentQuote: action.payload.quote
};
case SET_QUOTE_INITIAL_VALUES:
return {
...state,
quoteInitial: action.payload
};
default:
return state;
}
}
The initial values are set on the form when I look at it in the store. However, when I move to the next page, the values I entered that weren't set initially (for fields without initial values) aren't preserved and the only values in the store for that page are the initial ones again.
The values for fields on the final page ARE saved. Every form is setup to preserve form data like so (second page example)
export default reduxForm({
form: "quote", //Form name is same
destroyOnUnmount: false, // <------ preserve form data
forceUnregisterOnUnmount: true // <------ unregister fields on unmount
})(QuoteFormSecondPage);
Does anyone have any idea what would cause the form fields values not to be preserved.
Note, when I remove initial values the field values are preserved and do carry over!
Edit: Here is my quoteForm.component that displays each page/form
<div className="quote-container">
{page === 1 && (
<QuoteFormFirstPage
close={this.props.toggleModal}
onSubmit={this.nextPage}
currentPage={page}
initialValues={this.props.initialValues}
enableReinitialize={true}
/>
)}
{page === 2 && (
<QuoteFormSecondPage
close={this.props.toggleModal}
previousPage={this.previousPage}
onSubmit={this.nextPage}
currentPage={page}
initialValues={this.props.initialValues}
enableReinitialize={true}
/>
)}
{page === 3 && (
<QuoteFormThirdPage
close={this.props.toggleModal}
previousPage={this.previousPage}
toggleAlternateAddress={this.toggleAlternateAddress}
onSubmit={this.onSubmit}
currentPage={page}
showAlternateAddress={this.state.showAlternateAddress}
initialValues={this.props.initialValues}
enableReinitialize={true}
/>
)}
{page === 4 && (
<QuoteFormSuccessPage
close={this.props.toggleModal}
previousPage={this.previousPage}
currentPage={page}
/>
)}
</div>
Solved, in case anyone runs into the issue in future. I was passing
initialValues={this.props.initialValues}
enableReinitialize={true}
to each component, it only needs to be passed in once, to the first one.

How to determine active route in react router

I'm have a Redux app that uses react router with nested routes, like this:
<Provider store={store}>
<Router history={browserHistory}>
<Route name="sales" path="/" component={App}>
<IndexRoute name="home" component={Home} />
<Route name="reports" path="/reports" component={Reports}>
<IndexRoute name="reports-home" component={ReportsHome} />
<Route name="report-1" path="/reports/report-1" component={Report1}/>
<Route name="report-2" path="/reports/report-2" component={Report2}/>
</Route>
</Route>
</Router>
</Provider>
I'm trying to write a breadcrumbs component; so would like to be able to deterime the current route.
I've configured the component to receive the router using the withRouter function provided by react router:
Breadcrumbs = withRouter(Breadcrumbs);
This gives me a router object that looks like this:
Object {__v2_compatible__: true}
__v2_compatible__: true
createHref: createHref(location, query)
createKey: createKey()createLocation: createLocation(location)
createPath: createPath(location, query)
go: go(n)
goBack: goBack()
goForward: goForward()
isActive: isActive(location)
listen: listen(listener)
listenBefore: listenBefore(hook)
push: push(location)
pushState: ()
registerTransitionHook: registerTransitionHook(hook)
replace: replace(location)
replaceState: ()
setRouteLeaveHook: listenBeforeLeavingRoute(route, hook)
setState: ()
transitionTo: transitionTo(nextLocation)
unregisterTransitionHook: unregisterTransitionHook(hook)
__proto__: Object
Can I use this to determine the current route? Is there better way?
Getting location etc. via withRouter was added in react-router version 3.0. Dan Abramov recommends upgrading to 3.0 to use withRouter. From 2.7 to 3.0, it only provided the functions you saw.
Source: https://github.com/ReactTraining/react-router/blob/master/CHANGES.md#v300-alpha1
There is already a module that does this for you, I believe it's called react-router-breadcrumbs. I haven't tried it though.
If you want a custom solution, here's what you could do:
Use the this.props.routes and this.props.params objects. You can then map through the routes and for each entry make a lookup for such key in the params object. You can then create a string with said parameters.
Note that I have given each route (except IndexRoutes) a path attribute, because sometimes I want to display a custom name for a given page. For example:
<Route path="/:productId" name="Start" title="Start" component={StartView} />
Here's the solution on my app:
componentDidMount = () => {
this._prepareBreadCrumbs(this.props);
}
componentWillReceiveProps = (newProps) => {
this._prepareBreadCrumbs(newProps);
}
_prepareBreadCrumbs = (props) => {
let {routes, params} = props;
let breadcrumbPath = "";
let temp = routes.map(
(item, i) => {
if(item.path == null) return null; //if we are visiting an item without a path, ignore it.
if(i < routes.length-1 && routes[i+1].path != null) {
let arr = item.path.split(/[:/]|(:\/)/g); //sometimes the path is like "/:product_id/details" so I need to extract the interesting part here.
arr = arr.map(function(obj) {
return (obj in params) ? params[obj] : obj; //We now have ":product_id" and "details" - the first one will exist in the "params" object.
});
breadcrumbPath += arr.filter(Boolean).join("/") + "/"; //clean out some garbage and add the "/" between paths.
if(i == 0) return <li key={i}><Link to={breadcrumbPath}>YourSite.com</Link></li> //If on the root - display the site name
return <li key={i}><Link to={breadcrumbPath}>{item.name}</Link></li>
} else {
document.title = "YourSite.com - " + item.title; //set the document title if you want
if(i == 0) return <li key={i} className="active"><span>YourSite.com</span></li>
return <li key={i} className="active"><span>{item.name}</span></li>
}
}
);
this.setState({breadcrumbPath: temp});
}
render() {
<p>{this.state.breadCrumbPath || ""}</p>
}
You'd want to put this in your top-level React Component.

Resources