Subscribe inside useEffect doens't work in react-native - reactjs

I have the following code which I use to subscribe to branch.io. Here, the first console.log() is printed. But it doesn't go beyond that. What am I doing wrong here?
import React, { useEffect, useState } from 'react';
import { View } from 'react-native';
import branch from 'react-native-branch';
import {
Button,
InviteSummary,
ListItem,
ScreenContainer,
Seperator,
Label,
} from 'components';
import { APP_HOME_SCREEN } from 'common/constants/screens';
import { copyContent, shareContent } from 'library/Utils';
import ChevronRight from 'res/icons/Chevron/Right';
import Copied from 'res/icons/System/checked.svg';
import Copy from 'res/icons/System/Copy';
const SCREEN_TITLE = 'Invite and earn';
let _unsubscribeFromBranch = null;
const Invite = () => {
const [referralLink, setReferralLink] = useState('');
const [linkCopied, handleCopy] = useState(false);
useEffect(() => {
console.log('before subscribe');
_unsubscribeFromBranch = branch.subscribe(({ error, params }) => {
console.log('in subscribe');
if (error) {
console.error('Error from Branch: ', error);
return;
}
console.log('Branch params: ', JSON.stringify(params));
if (!params['+clicked_branch_link']) return;
});
}, []);
const handleCopyLink = (text: string) => copyContent(text, () => handleCopy(true));
const handleShareLink = () => shareContent(referralLink);
return (
<>
<ScreenContainer
title={SCREEN_TITLE}
backScreen={APP_HOME_SCREEN}
backScreenIcon={<ChevronRight />}>
<View>
<InviteSummary
upperMainText='S$40.00'
lowerMainText='earned'
leftSubText='7 invites earned'
rightSubText='3 pending'
/>
<Label
type={Label.Types.BODY_SMALL}
label='when you invite a friend to no matter how many friends! To make things sweeter, each friend you invite gets S$5 too.'
/>
<Label
type={Label.Types.BODY_SMALL}
label='Referral credit will be awarded to you automatically once your friend activates their Visa debit card.'
/>
<Seperator />
<Label
type={Label.Types.BODY_SMALL}
label='SHARE Your unique invite link'
/>
<ListItem
title={referralLink}
rightIcon={linkCopied ? <Copied /> : <Copy />}
disabled={linkCopied}
onPress={() => handleCopyLink(referralLink)}
/>
</View>
</ScreenContainer>
<View>
<Button
type={Button.Types.PRIMARY}
text='SHARE LINK'
disabled={!linkCopied}
onPress={handleShareLink}
/>
</View>
</>
);
};
export default Invite;

Related

Fullcalendar re-fetch events after change in React state

I have this really simple React component:
import { useState } from "react";
import FullCalendar from "#fullcalendar/react";
import dayGridPlugin from "#fullcalendar/daygrid";
import { getCalendarEvents } from "../../utils/frontend/getEvents";
import EventModal from "../CreateEventModal";
const Calendar = ({ calendar: calendarId, eventColor }) => {
const [isModalVisible, setModalVisibility] = useState(false);
return (
<>
<button onClick={() => setModalVisibility(true)}>Open Modal</button>
<FullCalendar
plugins={[dayGridPlugin]}
events={getCalendarEvents(calendarId)}
/>
<EventModal open={isModalVisible} onClose={() => setModalVisibility(false)} />
</>
);
}
export default Calendar;
Yet, there is a problem. When ever I open the modal, Fullcalendar decides to re-fetch my events. Here the code for getCalendarEvents:
export const getCalendarEvents =
(calendarId) => (info, successCallback, failureCallback) => {
fetch(`/list-events?id=${calendarId}&timeMin=${info.start}&timeMax=${info.end}`)
.then((res) => res.json())
.then((res) => {
if (res.success && res.data)
successCallback(
res?.data?.map((event) => ({
id: event.id,
title: event.summary,
location: event.location,
description: event.description,
start: event.start?.dateTime,
end: event.end?.dateTime,
})) || []
)
else {
failureCallback(new Error(res.error || "Something went wrong."));
}
})
};
I believe this is an issue with Fullcalendar itself. However, is there are way to fix this issue from my side?
I am using #fullcalendar/react version 5.10.1.
I believe this happens because every render is processes "events={getCalendarEvents(calendarId)}", which creates new reference to function.
Try something like that:
import { useState, useMemo } from "react";
import FullCalendar from "#fullcalendar/react";
import dayGridPlugin from "#fullcalendar/daygrid";
import { getCalendarEvents } from "../../utils/frontend/getEvents";
import EventModal from "../CreateEventModal";
const Calendar = ({ calendar: calendarId, eventColor }) => {
const [isModalVisible, setModalVisibility] = useState(false);
const memoizedEventsFn = useMemo(() => {
return getCalendarEvents(calendarId);
}, [calendarId]);
return (
<>
<button onClick={() => setModalVisibility(true)}>Open Modal</button>
<FullCalendar
plugins={[dayGridPlugin]}
events={memoizedEventsFn}
/>
<EventModal open={isModalVisible} onClose={() => setModalVisibility(false)} />
</>
);
}
export default Calendar;
I hope it works. If not, it is possible to memoize FullCalendar with memo or useMemo.

database get request output to display in return text- react native

We are trying to fetch the name value from the local database and storing it as tlName variable, we want to display the dynamic value of tlName in return function.
Currently, we can see the values in console.log but not in the emulator app screen and getting an error. How to resolve this issue?
import React , {useState} from 'react';
import { Text } from 'react-native';
//import { View } from 'react-native-web';
import logo from '../assets/Logo3.jpg';
import { StatusBar } from 'expo-status-bar';
import {
InnerContainer, PageLogo,
PageTitle,
SubTitle,
StyledFormArea,
StyledButton, ButtonText,
Line,
WelcomeContainer, WelcomeImage, Avatar
} from '../components/Styles';
import axios from 'axios';
const tlName = null;
const name = null;
const Dummy2 = ({navigation}) => {
/**************Test for get request */
setTimeout(() => {
console.log("testing get request")
const url = 'http://10.1.46.41:8090/api/users/';
axios
.get(url)
.then((response) => {
const result = response.data;
console.log(result[3].Name)
name= result[3].Name;
console.log(name)
tlName = name;
}).catch(error => {
console.log(error.JSON);
setSubmitting(false);
handleMesssage("An error occured. Check your network and try again");
});
}, 5000)
return(
<>
<StatusBar style = "light"/>
<InnerContainer>
<WelcomeImage source={require('../assets/Logo3.jpg')}/>
<WelcomeContainer>
<PageTitle welcome = {true}> Trial for getting data </PageTitle>
<SubTitle welcome = {true}>Your request has been sent to the Team lead of {tlName}</SubTitle>
<SubTitle welcome = {true}>Kindly await their response</SubTitle>
<StyledFormArea>
<Avatar source={require('../assets/Logo3.jpg')} />
<StyledButton onPress = {() => {navigation.navigate('Welcome')}}>
<ButtonText> Okay </ButtonText>
</StyledButton>
<Line />
</StyledFormArea>
</WelcomeContainer>
</InnerContainer>
</>
);
};
export default Dummy2;

ReactJs feedback with SVG Emojis

`
import React, { useEffect, useRef, useState, useCallback } from "react";
import Button from "../../elements/buttons/Button";
import Icon from "../../elements/icons/Icon";
function useDynamicSVGImport(name, options = {}) {
const ImportedIconRef = useRef();
const [loading, setLoading] = useState(false);
const [error, setError] = useState();
const { onCompleted, onError } = options;
useEffect(() => {
setLoading(true);
const importIcon = async () => {
try {
ImportedIconRef.current = (
await import(`./${name}.svg`)
).ReactComponent;
if (onCompleted) {
onCompleted(name, ImportedIconRef.current);
}
} catch (err) {
if (onError) {
onError(err);
}
setError(err);
} finally {
setLoading(false);
}
};
importIcon();
}, [name, onCompleted, onError]);
return { error, loading, SvgIcon: ImportedIconRef.current };
}
/**
* Simple wrapper for dynamic SVG import hook. You can implement your own wrapper,
* or even use the hook directly in your components.
*/
/** const Icon = ({ name, onCompleted, onError, ...rest }) => {
const { error, loading, SvgIcon } = useDynamicSVGImport(name, {
onCompleted,
onError
});
if (error) {
return error.message;
}
if (loading) {
return "Loading...";
}
if (SvgIcon) {
return <SvgIcon {...rest} />;
}
return null;
};
*/
export default function FacialReactions() {
const [name, setName] = useState("svg1");
const handleOnCompleted = useCallback(
(iconName) => console.log(`${iconName} successfully loaded`),
[]
);
const handleIconError = useCallback((err) => console.error(err.message), []);
return (
<div className="App">
<button
onClick={() =>
setName((prevName) => (prevName === "svg1" ? "svg2" : "svg1"))
}
>
Change Icon
</button>
<section>
<Icon icon="mood-vgood" variant="horizontal" />
<Icon icon="mood-good" variant="horizontal" />
<Icon icon="mood-neutral" variant="horizontal" />
<Icon icon="mood-bad" variant="horizontal" />
<Icon icon="mood-vbad" variant="horizontal" />
/** now i had to change the entire functionality to fit the attached Picture in the head of the question! */
/**
<Icon
name={name}
fill="gray"
onCompleted={handleOnCompleted}
onError={handleIconError}
/>
<Icon
name="svg1"
fill="gray"
width="300"
onCompleted={handleOnCompleted}
onError={handleIconError}
/>
<Icon
name="svg2"
fill="darkblue"
height="100"
onCompleted={handleOnCompleted}
onError={handleIconError}
/>
*/
</section>
</div>
);
}
`
I have been struggling with implementing the functionality of the attached design, a react feedback reaction using predefined SVG facial icons . But I can not figure out from where to start.
I have already the Icon set ready to use, however, I do not know how to come up with such functionality, How to add these SVG icons into a reusable react component!. now I had to change the entire functionality to fit the attached Picture in the head of the question!
please any help with that matter will be deeply appreciated! Thanks
You could just add onClick functionality to the svg.
<path onClick={()=>setMood(1)}></path>
For adding svg to react/jsx you can take a look at this https://blog.logrocket.com/how-to-use-svgs-in-react/

Is there a better way to get different data from an API in the same component based on data type?

I have a homepage with multiple sections, and I wanted each section to get different data, one for "Popular Recipes" and one for "New Recipes" for example. So I wrote a switch statement inside a useeffect for this but it feels not "the best way" to me.
Home.js:
import React from "react";
import { Container } from "#chakra-ui/react";
import Section from "./Section";
const sections = [
{
name: "Popular Recipes",
},
{ name: "New Recipes" },
];
function Home() {
return (
<Container maxWidth="full">
{sections.map((section, i) => (
<Section key={i} name={section.name} />
))}
</Container>
);
}
export default Home;
Section.js:
import React, { useEffect, useState } from "react";
import { Text, Flex, Box, Spacer, SimpleGrid, Heading } from "#chakra-ui/react";
import { getAllRecipes } from "../api";
import Recipe from "../common/Recipe";
function Section(props) {
const [recipes, setRecipes] = useState([]);
const [error, setError] = useState("");
useEffect(() => {
switch (props.name) {
case "Popular Recipes":
getAllRecipes()
.then((data) => {
setRecipes(data);
})
.catch((error) => setError(error.response.data.error));
}
}, []);
return (
<Box marginBottom="10">
<Heading as="h2" marginBottom="5" fontSize="xl" fontWeight="hairline">
{props.name}
</Heading>
<SimpleGrid minChildWidth="250px" columns={3} spacing={6}>
{error ? (
<Text>{error}</Text>
) : (
recipes?.map((recipe) => {
return <Recipe key={recipe._id} recipe={recipe} />;
})
)}
</SimpleGrid>
</Box>
);
}
export default Section;
Instead of depending on props.name, just have your Section component accept a getRecipes prop and pass it explicitly in the parent component. This decouples name from the actual implementation, so changing from Latest Recipes to Latest Posts (or whatever) won't break your app.
// queries.js -----
export const getPopularRecipes = () => fetch('/recipes/popular').then(res => res.json());
export const getLatestRecipes = () => fetch('/recipes/latest').then(res => res.json());
// Section.js -----
function Section(props) {
// ...
useEffect(() => {
props.getRecipes()
.then(data => setRecipes(data))
.catch(error => setError(error.response.data.error));
}, []);
// ...
}
// Home.js -----
import { getPopularRecipes, getLatestRecipes } from './queries';
function Home() {
return (
<Container maxWidth="full">
<Section name="Popular" getRecipes={getPopularRecipes} />
<Section name="Latest" getRecipes={getLatestRecipes} />
</Container>
);
}
Alternatively, if the logic for fetching recipes is identical (only the API path is different, for example), you can have the Section component accept a recipeType prop (or similar).
// Section.js -----
function Section(props) {
// ...
useEffect(() => {
fetch(`/recipes/${props.recipeType}`)
.then(res => res.json())
.then(data => setRecipes(data))
.catch(error => setError(error.response.data.error));
}, []);
// ...
}
// Home.js -----
import { getPopularRecipes, getLatestRecipes } from './queries';
function Home() {
return (
<Container maxWidth="full">
<Section name="Popular Recipes" recipePath="popular" />
<Section name="Latest Recipes" recipePath="latest" />
</Container>
);
}
Either way, decouple your network implementation from your name and you should be resilient to future refactors.

React Hide and Show a Component only using a Start button

I would like to hide two components in the Home component:
DisplayBox and GameBox.
When the user logs in the game starts automatically.
Instead, I'd like to only 'Show' the start button.
Then the user may press the Start button to start the game.
(will eventually have more levels to choose from in the start button component)
import React, { useState, useEffect } from "react";
import "./home.js";
import DisplayBox from '../components/displayBox';
import GameBox from '../components/gameBox/gameBox';
import randomWords from 'random-words'
import "./home.css";
const Home = () => {
const [numLetters, setNumLetters] = useState(5)
const [word, setWord] = useState("")
const [blank, setBlank ] = useState()
console.log("Blank", blank);
console.log("WORD", word)
const getARandomWord = () => {
setWord(randomWords(({ exactly: 1, maxLength: 4, formatter: (word) => word.toUpperCase() })))
}
useEffect(() => {
getARandomWord()
}, [])
function clickStart(){
// return { show: true};
// alert('You Start!');
}
return (
<>
<div>
<button onClick={clickStart} style={{width:"800px"}}>
START
</button>
</div>
<DisplayBox word={word} />
<GameBox numLetters={numLetters} setNumLetters={setNumLetters} word={word} setWord={setWord} getARandomWord={getARandomWord} />
</>
);
};
Home.propTypes = {};
export default Home;
create a new state to oversee whether the game is start or not then:-
Home.js:-
import React, { useState, useEffect } from "react";
import "./home.js";
import DisplayBox from '../components/displayBox';
import GameBox from '../components/gameBox/gameBox';
import randomWords from 'random-words'
import "./home.css";
const Home = () => {
const [numLetters, setNumLetters] = useState(5)
const [word, setWord] = useState("")
const [blank, setBlank ] = useState()
// state to track whether the game is start or not
const [isStart, setIsStart] = useState(false)
console.log("Blank", blank);
console.log("WORD", word)
const getARandomWord = () => {
setWord(randomWords(({ exactly: 1, maxLength: 4, formatter: (word) => word.toUpperCase() })))
}
useEffect(() => {
getARandomWord()
}, [])
// handle game start
function handleGameStart() {
if(isStart) {
// do something when game start
alert('Game starting!!!')
// reset the game again
// setIsStart(false)
} else {
console.log('Game is not starting!')
}
}
// function to oversee what happens after game start
useEffect(() => {
handleGameStart()
}, [isStart])
return (
<>
<div>
<button onClick={() => setIsStart(true)} style={{width:"800px"}}>
START
</button>
</div>
{/* this should only be available when game has started */}
{isStart && (
<>
<DisplayBox word={word} />
<GameBox numLetters={numLetters} setNumLetters={setNumLetters} word={word} setWord={setWord} getARandomWord={getARandomWord} />
</>
)}
</>
);
};
Home.propTypes = {};
export default Home;

Resources