React Native - {"name":"Invariant Violation","framesToPop":1} - javascript

I'm trying to implement meilisearch api in React native and it is working fine with my simulator and after I publish the app some of the users cannot see the data returning from meilisearch, the error is
{"name":"Invariant Violation","framesToPop":1}
This is my code
Meilisearch.js
import axios from 'axios';
import { meilisearchConfig } from '../Config';
const MeilisearchApi = async (payload, success, failed) => {
try {
const response = await axios({
method: 'post',
url: `${meilisearchConfig?.host}indexes/activities/search`,
data: payload,
headers: {
'X-Meili-API-Key': meilisearchConfig?.apiKey,
},
});
success?.(response?.data);
} catch (err) {
failed?.(err);
}
};
export default MeilisearchApi;
This is the normalizer for returning data
import moment from 'moment';
import { IActivity, IByDateGroupFilter } from 'reducers/types';
export const activityNormalizer = (state, { hits, offset }) => {
const {
melisearchActivityData: { byDate, dates, all },
} = state;
const isRefreshing = offset === 0;
const newAll = isRefreshing ? hits : [...all, ...hits];
const datesNew: string[] = isRefreshing ? [] : dates;
const byDateNew: any = isRefreshing ? {} : byDate;
const byDateGroup: IByDateGroupFilter[] = [];
hits.forEach((activity: IActivity) => {
const date = getFormattedDate(activity.created_at);
if (byDateNew[date]) byDateNew[date].push({ ...activity });
else {
byDateNew[date] = [{ ...activity }];
datesNew.push(date);
}
});
Object.keys(byDateNew).forEach((key) => {
byDateGroup.push({
title: key,
data: byDateNew[key],
});
});
return {
dates: datesNew,
byDate: byDateNew,
byDateGroup,
all: newAll,
};
};
This is how i call my Meilisearch API method
MeilisearchApi(
{
q: search,
filters: filters,
offset: newOffset,
limit: PAGE_SIZE,
},
({ hits }: { hits: any[] }) => {
setDataLoaded(true);
setMelisearchActivitiesToRedux({ hits, offset: newOffset });
if (newOffset === 0) {
sectionList?.current?.scrollToLocation({
itemIndex: 1,
});
}
},
(err: any) => {
setDataLoaded(true);
log(err)
},
);
No Idea how this error happens, when users kill the app and logging again this works fine

Related

Testing a react-query hook that uploads a binary file

There is a unit test that's failing on the following react-query hook.
The hook works fine otherwise, it uploads a binary file to an endpoint.
Here is the hook:
import { useMutation, useQueryClient } from 'react-query';
import { IUploadFieldRequest } from '../constants/types';
import { licenseServiceAPI } from '../services';
export const URI = () => '/licenses/upload';
export const useUploadLicense = () => {
const queryClient = useQueryClient();
const [apiService] = licenseServiceAPI<IUploadFieldRequest>({
headers: {
'Content-Type': 'multipart/form-data'
}
});
const {
data: response,
mutateAsync,
isLoading,
isSuccess
} = useMutation(
(formData: IUploadFieldRequest) => {
const form = new FormData();
Array.from(formData.files)?.forEach((file) => {
form.append('license-file', file);
});
return apiService.post(URI(), form);
},
{
onError: () => {
return queryClient.invalidateQueries('licenseUpload');
}
}
);
return {
data: response?.data,
uploadFiles: mutateAsync,
isLoading,
isSuccess
};
};
export default useUploadLicense;
And this is the test for it:
import { act, renderHook } from '#testing-library/react-hooks';
import { queryClientInstance } from '#oam/shared/test-utils';
import useUploadLicense from './use-upload-license';
const file = new File(['file'], 'license-file.json', { type: 'multipart/form-data' });
it('upload files', async () => {
const { result, waitFor } = renderHook(() => useUploadLicense(), {
wrapper: queryClientInstance
});
act(() => {
result.current.uploadFiles({ files: [file] });
});
await waitFor(() => expect(result.current.isLoading).toBeTruthy());
await waitFor(() => expect(result.current.isSuccess).toBeTruthy());
});
The error says it has Timed out in waitFor after 1000ms. and that's a Network error
Any ideas how to fix this?
Also, it could be useful to know the interface:
type IUploadFieldRequest = {
files: File[];
};
And the service:
import { AxiosRequestConfig } from 'axios';
import { requestConfig } from '#oam/shared/services';
import init, { Response } from '#oam/shared/utils/request-wrapper';
export const BASE_PATH =
process.env.NODE_ENV === 'development'
? '/myurl/v1'
: 'http://localhost:7999/v1';
export const licenseServiceAPI = <TRequest, TResponse = TRequest>(
config?: AxiosRequestConfig
): [Response<TRequest, TResponse>, AxiosRequestConfig] => {
const newConfig = requestConfig({
baseURL: BASE_PATH,
headers: {
Accept: 'application/json',
...config?.headers
},
responseType: config?.responseType ? config?.responseType : undefined
});
return [init(newConfig), newConfig];
};
export default licenseServiceAPI;

When routing mswjs/data populates the database with new items and removes the previous one, making it inaccessible

I use next-redux-wrapper, MSW, #mswjs/data and redux-toolkit for storing my data in a store as well as mocking API calls and fetching from a mock Database.
I have the following scenario happening to me.
I am on page /content/editor and in the console and terminal, I can see the data was fetched from the mock database and hydrated from getStaticProps of Editor.js. So now IDs 1 to 6 are inside the store accessible.
Now I click on the PLUS icon to create a new project. I fill out the dialog and press "SAVE". a POST request starts, it's pending and then it gets fulfilled. The new project is now in the mock DB as well as in the store, I can see IDs 1 to 7 now.
Since I clicked "SAVE" and the POST request was successful, I am being routed to /content/editor/7 to view the newly created project.
Now I am on Page [id].js, which also fetched data from the mock DB and then it gets stored and hydrated into the redux store. The idea is, it takes the previous store's state and spreads it into the store, with the new data (if there are any).
Now the ID 7 no longer exists. And IDs 1 to 6 also don't exist anymore, instead, I can see in the console and terminal that IDs 8 to 13 were created, and the previous ones are no more.
Obviously, this is not great. When I create a new project and then switch the route, I should be able to access the newly created project as well as the previously created ones. But instead, they all get overwritten.
It either has something to do with the next-redux-wrapper or MSW, but I am not sure how to make it work. I need help with it. I will post some code now:
Code
getStaticProps
// path example: /content/editor
// Editor.js
export const getStaticProps = wrapper.getStaticProps(
(store) =>
async ({ locale }) => {
const [translation] = await Promise.all([
serverSideTranslations(locale, ['editor', 'common', 'thesis']),
store.dispatch(fetchProjects()),
store.dispatch(fetchBuildingBlocks()),
]);
return {
props: {
...translation,
},
};
}
);
// path example: /content/editor/2
// [id].js
export const getStaticProps = wrapper.getStaticProps(
(store) =>
async ({ locale, params }) => {
const { id } = params;
const [translation] = await Promise.all([
serverSideTranslations(locale, ['editor', 'common', 'thesis']),
store.dispatch(fetchProjects()),
// store.dispatch(fetchProjectById(id)), // issue: fetching by ID returns null
store.dispatch(fetchBuildingBlocks()),
]);
return {
props: {
...translation,
id,
},
};
}
);
Mock Database
Factory
I am going to shorten the code to the relevant bits. I will remove properties for a project, as well es helper functions to generate data.
const asscendingId = (() => {
let id = 1;
return () => id++;
})();
const isDevelopment =
process.env.NODE_ENV === 'development' || process.env.STORYBOOK || false;
export const projectFactory = () => {
return {
id: primaryKey(isDevelopment ? asscendingId : nanoid),
name: String,
// ... other properties
}
};
export const createProject = (data) => {
return {
name: data.name,
createdAt: getUnixTime(new Date()),
...data,
};
};
/**
* Create initial set of tasks
*/
export function generateMockProjects(amount) {
const projects = [];
for (let i = amount; i >= 0; i--) {
const project = createProject({
name: faker.lorem.sentence(faker.datatype.number({ min: 1, max: 5 })),
dueDate: date(),
fontFamily: getRandomFontFamily(),
pageMargins: getRandomPageMargins(),
textAlign: getRandomTextAlign(),
pageNumberPosition: getRandomPageNumberPosition(),
...createWordsCounter(),
});
projects.push(project);
}
return projects;
}
API Handler
I will shorten this one to GET and POST requests only.
import { db } from '../../db';
export const projectsHandlers = (delay = 0) => {
return [
rest.get('https://my.backend/mock/projects', getAllProjects(delay)),
rest.get('https://my.backend/mock/projects/:id', getProjectById(delay)),
rest.get('https://my.backend/mock/projectsNames', getProjectsNames(delay)),
rest.get(
'https://my.backend/mock/projects/name/:id',
getProjectsNamesById(delay)
),
rest.post('https://my.backend/mock/projects', postProject(delay)),
rest.patch(
'https://my.backend/mock/projects/:id',
updateProjectById(delay)
),
];
};
function getAllProjects(delay) {
return (request, response, context) => {
const projects = db.project.getAll();
return response(context.delay(delay), context.json(projects));
};
}
function postProject(delay) {
return (request, response, context) => {
const { body } = request;
if (body.content === 'error') {
return response(
context.delay(delay),
context.status(500),
context.json('Server error saving this project')
);
}
const now = getUnixTime(new Date());
const project = db.project.create({
...body,
createdAt: now,
maxWords: 10_000,
minWords: 7000,
targetWords: 8500,
potentialWords: 1500,
currentWords: 0,
});
return response(context.delay(delay), context.json(project));
};
}
// all handlers
import { buildingBlocksHandlers } from './api/buildingblocks';
import { checklistHandlers } from './api/checklist';
import { paragraphsHandlers } from './api/paragraphs';
import { projectsHandlers } from './api/projects';
import { tasksHandlers } from './api/tasks';
const ARTIFICIAL_DELAY_MS = 2000;
export const handlers = [
...tasksHandlers(ARTIFICIAL_DELAY_MS),
...checklistHandlers(ARTIFICIAL_DELAY_MS),
...projectsHandlers(ARTIFICIAL_DELAY_MS),
...buildingBlocksHandlers(ARTIFICIAL_DELAY_MS),
...paragraphsHandlers(ARTIFICIAL_DELAY_MS),
];
// database
import { factory } from '#mswjs/data';
import {
buildingBlockFactory,
generateMockBuildingBlocks,
} from './factory/buildingblocks.factory';
import {
checklistFactory,
generateMockChecklist,
} from './factory/checklist.factory';
import { paragraphFactory } from './factory/paragraph.factory';
import {
projectFactory,
generateMockProjects,
} from './factory/project.factory';
import { taskFactory, generateMockTasks } from './factory/task.factory';
export const db = factory({
task: taskFactory(),
checklist: checklistFactory(),
project: projectFactory(),
buildingBlock: buildingBlockFactory(),
paragraph: paragraphFactory(),
});
generateMockProjects(5).map((project) => db.project.create(project));
const projectIds = db.project.getAll().map((project) => project.id);
generateMockTasks(20, projectIds).map((task) => db.task.create(task));
generateMockBuildingBlocks(10, projectIds).map((block) =>
db.buildingBlock.create(block)
);
const taskIds = db.task.getAll().map((task) => task.id);
generateMockChecklist(20, taskIds).map((item) => db.checklist.create(item));
Project Slice
I will shorten this one as well to the relevant snippets.
// projects.slice.js
import {
createAsyncThunk,
createEntityAdapter,
createSelector,
createSlice,
current,
} from '#reduxjs/toolkit';
import { client } from 'mocks/client';
import { HYDRATE } from 'next-redux-wrapper';
const projectsAdapter = createEntityAdapter();
const initialState = projectsAdapter.getInitialState({
status: 'idle',
filter: { type: null, value: null },
statuses: {},
});
export const fetchProjects = createAsyncThunk(
'projects/fetchProjects',
async () => {
const response = await client.get('https://my.backend/mock/projects');
return response.data;
}
);
export const saveNewProject = createAsyncThunk(
'projects/saveNewProject',
async (data) => {
const response = await client.post('https://my.backend/mock/projects', {
...data,
});
return response.data;
}
);
export const projectSlice = createSlice({
name: 'projects',
initialState,
reducers: {
// irrelevant reducers....
},
extraReducers: (builder) => {
builder
.addCase(HYDRATE, (state, action) => {
// eslint-disable-next-line no-console
console.log('HYDRATE', action.payload);
const statuses = Object.fromEntries(
action.payload.projects.ids.map((id) => [id, 'idle'])
);
return {
...state,
...action.payload.projects,
statuses,
};
})
.addCase(fetchProjects.pending, (state, action) => {
state.status = 'loading';
})
.addCase(fetchProjects.fulfilled, (state, action) => {
projectsAdapter.addMany(state, action.payload);
state.status = 'idle';
action.payload.forEach((item) => {
state.statuses[item.id] = 'idle';
});
})
.addCase(saveNewProject.pending, (state, action) => {
console.log('SAVE NEW PROJECT PENDING', action);
})
.addCase(saveNewProject.fulfilled, (state, action) => {
projectsAdapter.addOne(state, action.payload);
console.group('SAVE NEW PROJECT FULFILLED');
console.log(current(state));
console.log(action);
console.groupEnd();
state.statuses[action.payload.id] = 'idle';
})
// other irrelevant reducers...
},
});
This should be all the relevant code. If you have questions, please ask them and I will try to answer them.
I have changed how the state gets hydrated, so I turned this code:
.addCase(HYDRATE, (state, action) => {
// eslint-disable-next-line no-console
console.log('HYDRATE', action.payload);
const statuses = Object.fromEntries(
action.payload.projects.ids.map((id) => [id, 'idle'])
);
return {
...state,
...action.payload.projects,
statuses,
};
})
Into this code:
.addCase(HYDRATE, (state, action) => {
// eslint-disable-next-line no-console
console.group('HYDRATE', action.payload);
const statuses = Object.fromEntries(
action.payload.projects.ids.map((id) => [id, 'idle'])
);
state.statuses = { ...state.statuses, ...statuses };
projectsAdapter.upsertMany(state, action.payload.projects.entities);
})
I used the adapter to upsert all entries.

React 17: Error: Invalid hook call. Hooks can only be called inside of the body of a function component

The use case
I am writing a piece of code searching for movies' deatils into the https://themoviedb.org
the code is written in React 17.0.2 using yarn, visual studio code.
The issue
I am calling a function, from a useEffect, that will generate a json file from the result of the call to TheMovieDB
I got the following error, could you help me? **Error: Invalid hook call. Hooks can only be called inside of the body of a function component. **
Thank you in advance for the time you will invest on this issue
useEffect in browser_movie_db.js
useEffect(() => {
if (slideRows.length > 0 && searchTerm.length > 2) {
const searchResult = searchInTheMovieDB(category, searchTerm);
if (searchResult.results.length > 0) {
setSlideRows(searchResult);
} else {
setSlideRows(slides[category]);
}
} else {
setSlideRows(slides[category]);
console.log("slideRows", slideRows);
setSlideRows(slides[category]);
}, [category, searchTerm, slideRows, slides, theMovieDBApikey]);
}
searchInTheMovieDB function in selection-filter-themoviedb.js
export function searchInTheMovieDB(media_type, query) {
let result = {};
if (media_type === "tv") {
result = {
tv: [
{
title: `${media_type} search result`,
data: GetSearch(media_type, query),
},
],
};
} else {
result = {
movie: [
{
title: `${media_type} search result`,
data: GetSearch(media_type, query),
},
],
};
}
return result;
}
GetSearch function in selection-filter-themoviedb.js
export function GetSearch(media_type, query) {
const [content, setContent] = useState([]);
useEffect(() => {
console.log("GetSearch", `${endpoint.Search}/${media_type}?query=${query}`);
api
.get(`${endpoint.Search}/${media_type}`, {
params: {
api_key,
query,
},
})
.then((res) => {
setContent(res.data.results);
})
.catch((error) => {
console.error("error.message", error.message);
});
}, [media_type, query]);
return { [media_type]: content };
}
Error Invalid hook call.
the solution
I have increased the amount of useEffects to identify the process :
the *searchTerm changes
then, the web services is called
eventually, the slideRows variable is set with a new content
PS: I have created an async function fetching the data from the webservice
const [searchResults, setSearchResults] = useState([]);
import axios from "axios";
const api = axios.create({ baseURL: BASE_URL });
async function getSearch(media_type, query) {
api
.get(`${endpoint.Search}/${media_type}`, {
params: {
api_key,
query,
},
})
.then((res) => {
setSearchResults(res.data.results);
})
.catch((error) => {
console.error("error.message", error.message);
});
}
useEffect(() => {
if (searchResults.length > 0) {
setSlideRows({
tv: [
{
title: `${category} search result`,
data: searchResults,
},
],
});
}
}, [category, searchResults]);
useEffect(() => {
if (slideRows.length > 0 && searchTerm.length > 2) {
getSearch(category, searchTerm);
} else {
setSlideRows(slides[category]);
}
}, [searchTerm, slides]);
useEffect(() => {
if (searchResults.length > 0 && searchResults.results?.length > 0) {
setSlideRows(searchResults);
} else {
setSlideRows(slides[category]);
}
setSlideRows(slides[category]);
}, [searchResults]);

why componentdidmount called two times

I have React Component in componentDidMount fetch data from the server. The issue is componentDidMount called twice also the API called twice. I have a view increment API like youtube video views increment twice in the database because of twice API calling.
class SingleVideoPlay extends React.Component {
constructor(props) {
super(props);
this.player = React.createRef();
}
state = {
autoPlay: true,
relatedVideos: [],
video: null,
user: null,
comments: [],
commentInput: {
value: '',
touch: false,
error: false
},
following: false,
tab: 'comments'
};
_Mounted = false;
componentDidMount() {
this._Mounted = true;
if (this._Mounted) {
const videoId = this.props.match.params.id;
this.getVideoDetails(videoId);
}
}
componentWillUnmount() {
this._Mounted = false;
try {
clearInterval(this.state.videoInterval);
this.props.videoEditUrl('');
} catch (error) {}
}
captureVideoTime = async () => {
const { video } = this.state;
const result = await updateWatchTime({
id: video._id,
time: 1
});
if (result.status === 200) {
const updateVideo = {
...video,
secondsWatched: video.secondsWatched + 1
};
this.setState({ video: updateVideo });
}
};
videoEnded = () => {
clearInterval(this.state.videoInterval);
};
videoPause = () => {
clearInterval(this.state.videoInterval);
};
loadVideo = () => {
clearInterval(this.state.videoInterval);
};
playingVideo = () => {
const interval = setInterval(this.captureVideoTime, 1000);
this.setState({ videoInterval: interval });
};
getVideoDetails = async (videoId) => {
const video = await getVideo(videoId);
if (video.status === 200) {
let response = video.data;
if (this.props.userId)
if (response.user._id === this.props.userId._id)
this.props.videoEditUrl(`/video/edit/${response.media._id}`);
this.setState({
relatedVideos: response.videos.docs,
video: response.media,
user: response.user
});
this.checkIsFollowing();
this.updateVideoStat(response.media._id);
}
};
updateVideoStat = async (id) => videoView(id);
checkIsFollowing = async () => {
const { userId } = this.props;
const { video } = this.state;
if (userId && video) {
const response = await isFollow({
follower: userId._id,
following: video._id
});
if (response) {
this.setState({ following: response.following });
}
}
};
addOrRemoveFollowing = async () => {
this.checkIsFollowing();
const { following, video } = this.state;
const { userId } = this.props;
if (userId) {
if (following) {
const response = await removeFollow({
follower: userId._id,
following: video._id
});
this.setState({ following: false });
} else {
const response = await addFollow({
follower: userId._id,
following: video._id
});
this.setState({ following: true });
}
}
};
submitCommentHandler = async (event) => {
const { userId } = this.props;
event.preventDefault();
if (userId) {
const result = await saveComment({
mediaId: this.state.video._id,
parentId: '0',
userID: userId._id,
userName: userId.username,
comment: this.state.commentInput.value
});
console.log(result);
if (result.status === 200) {
this.getVideoComments();
this.setState({ commentInput: { value: '', touch: false, error: false } });
}
}
};
render() {
const { autoPlay, relatedVideos, video, user, comments, commentInput, following, tab } = this.state;
const { userId } = this.props;
return (
<div className="container-fluid">
some coponents
</div>
);
}
}
const mapStateToProps = (state) => ({
userId: state.auth.user
});
export default connect(mapStateToProps, { videoEditUrl })(SingleVideoPlay);
I don't know why componentDidMount called two times alse it shows memmory lecage issue.
How to Fix it.
Multiple componentDidMount calls may be caused by using <React.StrictMode> around your component. After removing it double calls are gone.
This is intended behavior to help detect unexpected side effects. You can read more about it in the docs. It happens only in development environment, while in production componentDidMount is called only once even with <React.StrictMode>.
This was tested with React 18.1.0
I think the issue exists on the parent component that used SingleVideoPlay component. Probably that parent component caused SingleVideoPlay component rendered more than once.
Also, there is an issue on your code.
componentDidMount() {
this._Mounted = true;
if (this._Mounted) {
const videoId = this.props.match.params.id;
this.getVideoDetails(videoId);
}
}
Here, no need to check if this._Mounted, because it will always be true.
1.Install jQuery by
npm i jquery
import $ from 'jquery'
create your function or jwuery code after the export command or put at the end of the file

Repurpose Firebase's Facebook Login ID as a Data Key?

How can I utilize a user's Firebase ID ("id" : "1234567890") as a key for a separate data set within the same db?
the following would be my login detail where I would pull the ID from:
"users": {
"1234567890abcdefghijklmnopqrstuvwxyz" : {
"dp" : "https://www.profilePic.com",
"first_name" : "John",
"id" : "1234567890",
"last_name" : "Doe",
"token" : "abcdefghijklmnopqrstuvwxyz1234567890",
"uid" : "987654321"
}
}
Within Firebase Functions I currently have my code as:
admin.database().ref('location_config/{id}').set({current_location:[34.047220, -118.443534]})
The result currently comes out as:
"location_config": {
"{id}": {
"current_location": [34.047220, -118.443534]
}
}
But this is how I would like the data to appear with the ID being the key:
"location_config": {
"1234567890": {
"current_location": [34.047220, -118.443534]
}
}
The screenshot below illustrates how the UID is dynamic while the ID is constant.
Here is the code within Firebase:
let fbLocation;
module.exports = (event) => {
event.geoFire = functions.database.ref('users').onUpdate(event => {
admin.database().ref('/portal_coordinates_all').once('value', snapshot =>{
fbLocation = snapshot.val();
console.log ("snapshot", fbLocation);
}).then(() => {
// Create a Firebase reference where GeoFire will store its information
let firebaseRef = admin.database().ref('geofire');
// Create a GeoFire index
let geoFire = new GeoFire(firebaseRef);
geoFire.set(fbLocation)
.then(() => {
console.log("Provided key has been added to GeoFire");
}).catch(err => console.log(err))
.then(() => {
let geoQuery = geoFire.query({
center: [34.047220, -118.443534],
radius: 2
});
let locations = [];
let onKeyEnteredRegistration = geoQuery.on("key_entered", function(key, location, distance) {
locations.push(location);
});
// fires once when this query's initial state has been loaded from the server.
let onReadyRegistration = geoQuery.on("ready", function() {
console.log("GeoQuery has loaded and fired all other events for initial data");
console.log(locations);
// ******* here is where I'm having the issue *******
admin.database().ref( 'location_config/`${id}`' ).set( {current_location: locations} )
// **************************************************
// Cancel the "key_entered" callback
onKeyEnteredRegistration.cancel();
});
}).catch(err => console.log(err))
})
})
}
And here is the code within React Native:
import React, { Component } from 'react';
import {
StyleSheet,
View,
ActivityIndicator,
Button
} from 'react-native';
import firebase from 'firebase';
import { connect } from 'react-redux';
import { loginSuccess } from '../actions/AuthActions';
const FBSDK = require('react-native-fbsdk');
const { LoginManager, AccessToken } = FBSDK;
class Login extends Component {
constructor(props) {
super(props);
this.state = {
showSpinner: true,
};
}
componentDidMount() {
this.fireBaseListener = firebase.auth().onAuthStateChanged(auth => {
if (auth) {
this.firebaseRef = firebase.database().ref('users');
this.firebaseRef.child(auth.uid).on('value', snap => {
const user = snap.val();
if (user != null) {
this.firebaseRef.child(auth.uid).off('value');
this.props.loginSuccess(user);
}
});
} else {
this.setState({ showSpinner: false });
}
});
}
onPressLogin() {
this.setState({ showSpinner: true })
LoginManager.logInWithReadPermissions([
'public_profile',
'user_birthday',
'email',
'user_photos'
])
.then((result) => this.handleCallBack(result),
function(error) {
alert('Login fail with error: ' + error);
}
);
}
handleCallBack(result) {
let that = this;
if (result.isCancelled) {
alert('Login canceled');
} else {
AccessToken.getCurrentAccessToken().then(
(data) => {
const token = data.accessToken
fetch('https://graph.facebook.com/v2.8/me? fields=id,first_name,last_name&access_token=' + token)
.then((response) => response.json())
.then((json) => {
const imageSize = 120
const facebookID = json.id
const fbImage = `https://graph.facebook.com/${facebookID}/picture?height=${imageSize}`
this.authenticate(data.accessToken)
.then(function(result) {
const { uid } = result;
that.createUser(uid, json, token, fbImage)
});
})
.catch(function(err) {
console.log(err);
});
}
);
}
}
authenticate = (token) => {
const provider = firebase.auth.FacebookAuthProvider;
const credential = provider.credential(token);
return firebase.auth().signInWithCredential(credential);
}
createUser = (uid, userData, token, dp) => {
const defaults = {
uid,
token,
dp
};
firebase.database().ref('users').child(uid).update({ ...userData, ...defaults });
}
render() {
return (
this.state.showSpinner ? <View style={styles.container}><ActivityIndicator animating={this.state.showSpinner} /></View> :
<View style={styles.container}>
<Button
onPress={this.onPressLogin.bind(this)}
title="Login with Facebook"
color="#841584"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
const mapStateToProps = (state) => {
console.log('mapStateToProps', state);
return {
logged: state.auth.loggedIn,
user: state.auth.user
};
};
export default connect(mapStateToProps, { loginSuccess })(Login);
After banging my head against the wall as to why I couldn't grab the Facebook ID, it turned out that there isn't a need to grab this ID since Firebase's UID stays constant. I was unaware that the Firebase ID didn't change because in my test environment I would always login resulting in a new UID being created.

Categories

Resources