Global state in React Native in first load of the App - javascript

so im trying to make a App that loads all the thing in the loading screen/splash/auth screen.
So I in the basic I have a Welcome, Login and Home screen for now.
Welcome will be showing if the App is open for first time, Login if the user is opening the App without login or is logged out before closing the App and Home will be open if the user is logged in.
Here is the simply check:
componentDidMount() {
AsyncStorage.getItem("alreadyLaunched").then(value => {
if (value == null) {
AsyncStorage.setItem('alreadyLaunched', 'true'); // No need to wait for `setItem` to finish, although you might want to handle errors
this.setState({ firstLaunch: 'true' });
}
else {
this.setState({ firstLaunch: 'false' });
}
})
}
loadApp = async () => {
const userToken = await AsyncStorage.getItem('userToken')
setTimeout(
() => {
if (this.state.firstLaunch == 'true') {
this.props.navigation.navigate('Welcome')
} else {
if (userToken) {
this.props.navigation.navigate('App')
} else {
this.props.navigation.navigate('SignIn')
}
}
}, 0
);
}
And if the login is correct I just put this on Async:
AsyncStorage.setItem("userToken", "logged");
This for now its working perfectly, but I need to get 3-4 for functions to get information to the server then State It. Here is one of the functions:
getSignalsCount = async (username, password) => {
try {
var DeviceInfo = require('react-native-device-info');
//AUTH
fetch(Config.SERVER_URL + '/mob/auth', {
method: 'POST',
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
code: "1",
id: "",
sessId: "",
data: {
u: username,
p: password,
}
})
}).then((response) => response.json())
.then((res) => {
let res_code = res['code'];
let session = "";
let cl_id = 0;
let name = "";
if (res_code == 51) {
session = res['session'];
cl_id = res["data"]["response"]["client_id"];
name = res["data"]["response"]["name"];
this.setState({fullName:name});
//GET STATS
fetch(Config.SERVER_URL + '/mob/sport/getSignalsInfo', { //home
method: 'POST',
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
code: "9",
sessId: session,
data: {
devId: '1234567890',
devName: DeviceInfo.getUniqueID(),
u: username,
client_id: cl_id,
type: {
nums: 50000,
period: 12
}
}
})
}).then((response) => response.json())
.then((res) => {
var closed = res["data"]["response"]["data"]["closed"];
var opened = res["data"]["response"]["data"]["open"];
this.setState({ total_active_signals: opened, total_closed_signals: closed })
})
.done();
}
})
.done()
}
};
So if set this function on the Auth.js and then use it on the same screen with {this.state.total_active_signals} will show me the varible.
But I need it to be show also on HOME or maybe LOGIN and other pages that I may created in future. So I basic need this state to be use on maybe every screen.
I tried to create a global.js with:
module.exports = {
username: '',
};
And then in HOME:
//.....
import global from '../../global'
//....
<Text>Username: {GLOBAL.username}</Text>
But the question now is how to fill the global.js with the state so that, Home/Login/Profile/Stuff screens to get it later.
Thanks!

You basically have two options:
Use Redux and create an app wide state (store) to which all
components have access to (recommended). Your idea of a global state
fits pretty good with the concept of redux.
Pass data as props between components. You can also use a navigation library
to deal with this (e.g. react navigation or react native router
flux)

Related

Expo react native push notifications webview move

Nice to meet you.
I'm junior developer.
I made hybrid app(webview) by react natvie expo + php + mysql
this application have a push notification.
When I click on this, I want to go to a specific page. This page should go to a specific page, not the main page, not open browser by chrome, Safari... when the app opens.
how to make it?? please, help me.
Below is the code I made.
useEffect(() => {
registerForPushNotificationsAsync().then((token) => {
let url = "myUrl";
fetch(url, {
method: "POST",
headers: {
"Content-Type": "multipart/form-data",
},
body: JSON.stringify({ token: token }),
})
.then()
.catch((err) => console.log(err));
setExpoPushToken(token);
});
notificationListener.current =
Notifications.addNotificationReceivedListener((notification) => {
setNotification(notification);
});
responseListener.current =
Notifications.addNotificationResponseReceivedListener((response) => {
const url = response.notification.request.content.data.url;
// console.log(url); i can see specific url.
Linking.openURL(url);
// IntentLauncher.startActivityAsync("android.intent.action.View", {data: url});
});
return () => {
if (
typeof notificationListener.current !== "undefined" &&
typeof responseListener.current !== "undefined"
) {
Notifications.removeNotificationSubscription(
notificationListener.current
);
Notifications.removeNotificationSubscription(responseListener.current);
}
};
}, []);

Dynamic router and page with Next.js and Prisma

I have cards with product information in my database, I display them successfully on the user's page. Now I want to add a more details button on each card to go to a new page from it (/pages/card/[id]). But I don't really understand how I can pull out the card value by clicking through my API.
const res = await fetch('/api/cards/' + id, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ id: id })
})
if (res.ok) {
const result = await (await res).json()
if (result.redirectUrl) {
router.push(result.redirectUrl as string)
}
}
}
API
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { id } = req.query
if (req.method === 'GET') {
if (typeof id === 'string') {
const moreDetail= await db.sales.findUnique({
where: {
id: id },
})
res.send({ redirectUrl: '/card'+[id] })
}
}
My card in schema
id String #id #default(cuid())
title String
description String
active Boolean #default(true)
My suggestion would be to introduce another API endpoint that returns an array of all of the available cards, or at least an array of all of the available card ids. After that, create a new page matching your URL format /pages/card/[id].tsx and inside that file, create your page like normal, but also export 2 functions:
getStaticPaths
getStaticProps
These let Next know what paths are available and how to load data for them during the build process.
export async function getStaticPaths() {
const cardIds = await fetch('/api/cards', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
});
return {
paths: cardIds.map((id) => (
{
params: { id }
},
)),
fallback: false, // setting to false will throw a 404 if none match
};
}
This lets Next know all of the available dynamic routes to generate pages for.
export async function getStaticProps({ params: { id } }) {
const card = await fetch(`/api/cards/${id}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
});
return {
props: {
card,
},
}
}
This actually loads the data from your API given a card id and passes it into your component to display more details for.
Hopefully that gives you a good jumping off point.

ReactJS: Update values without reload the page

I have this problem, when I do a insert or a change about some data, to see the new data I need to reload the page while I would to update automatically the value without the need to reload the page. How can I do?
This is the part where the user click on submit and the post
_onSubmit(Document)
{
const self = this
if ( !_.isEmpty(Document) )
{
//..
if (Document && !_.isEmpty(Document.Anagraphics))
{
alertify.confirm(
utility.t('sureYouWanna_SAVE'),
() => {
const now = new Date();
Document._id = `PRODUCT:${new Date().getTime()}-${utility.CUID()}`
Document.CreationDate = now.toISOString()
Document.CategoryCode
Document.Status = 'New';
Document.Type = 'PRODUCT';
self._POST(Document)
},
function(){}
).set('labels', {ok: utility.t('YES_SAVE'), cancel: utility.t('CANCEL')})
}
else
{
$methods.WarnMissingValues()
}
}
else {
$methods.WarnMissingValues()
}
}
_POST(Document)
{
console.log("DOCUMENT POST", Document)
const self = this
const auth = this.props.db.auth
fetch(`${this.props.db.couch_db_host_url}requests`,{
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Basic ' + btoa(`${auth.username}:${auth.password}`)
},
body: JSON.stringify(Document)
})
.then(response => {
alertify.dismissAll()
if(response.status > 299 || response.status < 200){
alertify.error(utility.t('AN_ERROR_OCCURRED'))
self._updateState({ submitSucceeded: false })
}
else{
alertify.alert(utility.t('ITEM_EDITED_OK'), function(){})
self.props.history.push({
pathname: RoutesIT.products_details
})
}
})
.catch((err, warning) => {
if (err)
{
alertify.dismissAll()
alertify.error(utility.t('AN_ERROR_OCCURRED'))
console.log('_POST', err);
self._updateState({ submitSucceeded: false })
}
else
{
console.log(warning)
alertify.dismissAll()
alertify.warning(utility.t(warning))
}
})
}
How can I do to not reload the page to see the result of the post? Thank you
UPDATE:
In the page I have also:
function mapStateToProps(state) {
const { app: { login, p, c, l, c_timestamp, p_timestamp, l_timestamp }, form } = state;
return {
db: login ? login.db : null,
Sender: login ? login.Location : null,
timestamp: login ? login.timestamp : null,
[ FORM_NAME ]: form[FORM_NAME],
products: p,
locations: l,
categories: c,
categories_timestamp: c_timestamp,
products_timestamp: p_timestamp,
locations_timestamp: l_timestamp,
utente: login,
};
}
while the reducers
case actions.CATE_UPDATE:
{
return {
...state,
c: action.payload,
c_timestamp: new Date().getTime()
}
}
For what I can see in your code, the problem may lie in the fact that you're not dispatching any action when you submit the data.
Redux store can only be modified via actions, and since you're not triggering any, its contents are never being updated. This explains why your component is not updated in real time: your local data is never changing, so React is not aware of any updates. Things works when you reload the page because you're probably fetching the data from server, where the data did change during your POST request.
In order to fix this issue, you first need to pass a mapDispatchToProp to the your component, same as what you did with mapStateToProps:
connect(mapStateToProps, mapDispatchToProps)(YourComponent);
Inside of mapDispatchToProps, you have to return a property containing a function that will dispatch the CATE_UPDATE action you want to run:
const mapDispatchToProps = (dispatch) => ({
cateUpdateAction: (payload) => dispatch({
type: CATE_UPDATE,
payload
}),
});
Once you've done that, you'll be able to access this function from your component's props and call it inside of your _POST method.
if (response.status > 299 || response.status < 200){
alertify.error(utility.t('AN_ERROR_OCCURRED'))
self._updateState({ submitSucceeded: false })
} else {
alertify.alert(utility.t('ITEM_EDITED_OK'), function(){})
// Dispatch action to update data in Redux store
self.props.cateUpdateAction(data_to_save);
self.props.history.push({
pathname: RoutesIT.products_details
})
}

Access this from another class React Native

I am currently factoring my code so as not to repeat the same lines x times, so I created a Functions.js file which I use to call functions from other classes. The problem is, that I cannot execute the function while keeping the properties of this to carry out setState, redirects etc. Here is an example, it will be more telling:
Class in functions.js :
export class KoHttpRequest extends React.Component {
constructor(props) {
super(props);
this.postRequest = this.postRequest.bind(this);
}
postRequest = async(url, json, accessToken) => {
this.setState({loaded: false, validatingAction: false});
fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization' : 'Bearer '.concat(accessToken)
},
body: json
}).then((response) => {
if (response.ok === true) {
this.fetchData().then(() => {
this.setState({loaded: true}, () => {
this.userIsValidatingAnAction();
setTimeout(() => {this.setState({validatingAction: false})}, 1000);
});
})
} else {
let error = JSON.stringify(response.headers.map);
this.props.navigation.navigate('Accueil', {failedAction: true, errorReason: error.split('"error-reason":').pop().split('}},')[0].concat(' URL : '.concat(response.url))});
}
})
.catch((error) =>{
console.error(error);
});
}
render() {
return null;
}
}
And in the file where I want to call it :
import { KoHttpRequest } from '../Components&Functions/Koust.js';
createNewInvoice = () => {
new KoHttpRequest().postRequest('https://koupp.com/apex/rest/mobile/facture', JSON.stringify({
type:'C',
numero:this.state.invoiceNumber,
date_liv:this.state.pickedDate,
provider_id:this.state.selectedProvider
}), this.state.accessToken);
};
So, to explain clearly, in the class, the .then() and .error() are same for all request I do in my app, that's why I need the code here and not in the class that is calling it.
Unfortunely, I don't understand how I can tell the function that the 'this' referenced to use is in the other component. Cause actually the function is using themselve props..
Thanks for help.
I'm just trying to access the setState of class that is calling it.
In Functions.js, when it's using setState, I want it set the state of the other class actually
EDIT :
I think I found a solution using a callback.
createNewInvoice = () => {
this.setState({loaded: false, validatingAction: false}, () => {
new KoHttpRequest().koPostRequest('https://koupp.com/apex/rest/mobile/facture', JSON.stringify({
type:'C',
numero:this.state.invoiceNumber,
date_liv:this.state.pickedDate,
provider_id:this.state.selectedProvider
}), this.state.accessToken, this.props, function(result) {
if (result.status === 200) {
this.fetchData().then(() => {
this.setState({loaded: true}, () => {
this.userIsValidatingAnAction();
setTimeout(() => {this.setState({validatingAction: false})}, 1000);
});
})
}
});
});
};
But now, that's the callback function that can't access "this".
EDIT_2:
Find it. Just need to replace function() {..} by () => {..}
Thanks!

React testing onSubmit using axios

I recently started testing my React app. However, I stumbled when dealing with submitting forms. My test covers most of the lines but misses out on actual part of submit form method.
LoginForm.js - submit form
const userLoginData = {
userId : this.state.userId,
password : this.state.password,
userType : this.state.userType
};
axios({
data : JSON.stringify(userLoginData),
type : 'post',
url : Constant.BASE_URL_SERVER+'/rest/login',
headers : {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
cache : false
})
.then(function (response) {
//alert("Form Submitted.");
this.setState({isLoggedIn : true});
this.setState({loginResponse : "Login Success!"});
if(this.state.userType === 'Customer'){
...
login_form-test.js
describe('testing form submission onSubmit', () => {
const testData = {
userId: '00000000',
password: 'SamplePassword0',
userType: 'Customer',
validForm: true,
}
it('should submit form onSubmit()', () => {
const mountedComponentHandle = mount(<LoginForm {...testData}/>);
const onSubmitForm = sinon.spy(
mountedComponentHandle.instance(),
'handleSubmitForm'
);
mountedComponentHandle.update();
const formHandle = mountedComponentHandle.find('form');
expect(formHandle.length).toBe(1);
formHandle.simulate('submit');
expect(onSubmitForm.called).toBe(true);
});
});
Please suggest on how to test .then() and .catch() of axios.
Thanks.
Key here is to make your code "testable". Separating responsibility helps to make your code more testable, readable and easy to maintain. In your case logic to post data over an API lies in some service which will handle api requests for your app, and you can test it separately.
Coming back to your question, I am providing you one of the possible solutions for testing async calls in your case:
// apiGateway.js
const postData = (url, data) => (
axios({
data: JSON.stringify(data),
type: 'post',
url: BASE_URL_SERVER + url,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
cache: false
})
);
Again you can test above code separately.
// myAppApi.js
const postLoginForm = (data, callback, errorCallback) => {
return postData('/rest/login', data)
.then((response) => callback(response.data))
.catch((error) => errorCallback(error))
};
// myAppApi.test.js
// import * as myAppApi from '../myAppApi'
it('should call callback when response is successful', async () => {
const mockResponse = {};
const mockRequestData = {};
const mockSuccessCallback = jest.fn();
const mockErrorCallback = jest.fn();
spyOn(myAppApi, 'postLoginForm').and.returnValue(Promise.resolve(mockResponse));
await myAppApi.postLoginForm(mockRequestData, mockSuccessCallback, mockErrorCallback);
expect(mockSuccessCallback).toHaveBeenCalled();
});
it('should call error callback when response is failed', async () => {
const mockRequestData = {};
const mockSuccessCallback = jest.fn();
const mockErrorCallback = jest.fn();
spyOn(myAppApi, 'postLoginForm').and.returnValue(Promise.reject());
await myAppApi.postLoginForm(mockRequestData, mockSuccessCallback, mockErrorCallback);
expect(mockErrorCallback).toHaveBeenCalled();
});
In above tests you can use different mocking methods or libraries.
And finally your component will look something like this
// LoginForm.js
class LoginForm extends React.Component {
onSuccessfulLogin(responseData) {
//.. success logic here
}
onFailedLogin(error) {
//.. error logic here
}
onSubmitForm(event) {
postLoginForm(this.state.data, this.onSuccessfulLogin, this.onFailedLogin)
}
}
As you can see separating out logic helps in testing. Further it will save you from ending up with component with tons of code in it. You can test your component for its state and presentation.
Hope this answers your question!

Categories

Resources