react-native Native element detection - javascript

I want in my react-native app detect if user have a touch ID sensor or not and then if he have I want to display button with native element action instead of just normal action button. When I created if statement it shows me an error. I'm using 'create-react-native-app' with expo client SDK.
error message
Code
class LoginButton extends React.Component {
state = {
waiting: false,
};
render() {
let authFunction;
if (NativeModules.ExponentFingerprint.hasHardwareAsync() === true) {
if (Platform.OS === 'android') {
authFunction = async () => {
this.setState({waiting: true});
try {
let result = await NativeModules.ExponentFingerprint.authenticateAsync();
if (result.success) {
alert('Udało Ci się zalogować')
} else {
alert('Logowanie nie udane')
}
}
finally {
this.setState({waiting: false})
}
};
} else if (Platform.OS === 'ios') {
authFunction = async () => {
let result = await NativeModules.ExponentFingerprint.authenticateAsync(
'Zaloguj się przy użyciu TouchID'
);
if (result.success) {
alert('Udało Ci się zalogować')
} else {
alert('Logowanie nie udane')
}
};
}
return (
<Button onPress={authFunction} title="Zaloguj się przy użyciu odcisku palca" style={styles.buttonStyle}>
{this.state.waiting
? 'Czekam na TouchID...'
: 'Zalogowano przy użyciu TouchID'}
</Button>
)
} else if (NativeModules.ExponentFingerprint.hasHardwareAsync() === false) {
return (
<Button onPress={} title="Zaloguj się" style={styles.buttonStyle}/>
)
}
}
}

The issue is here
<Button
onPress={} <--- here
title="Zaloguj się"
style={styles.buttonStyle}
/>
React doesn't allow you to assign empty expressions to JSX attributes.
In order to fix it, just remove it
<Button title="Zaloguj się" style={styles.buttonStyle}/>
or assign it, for example, to authFunction which will be null.
<Button onPress={authFunction} title="Zaloguj się" style={styles.buttonStyle}/>

Related

Problem with React Joyride on production Cannot read properties of undefined (reading '0')

We decided to implement a simple onboarding tour on our application, everything was going well on local but once we deploy it to production its crashing showing the next error on devtools:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading '0')
it's kind of hard to debug this and see what is the problem because it's caused on production and its like debugging with the eyes closed.
the bug/error that the console show it's and undefined and by logic will crash too on local, but this it's not the case
The error is caused after we pass the first step like you can see on this video(also you can se how it's working locally:
VIDEO WITH THE ISSUE
They way the joyride its implemented consists on two big keys
the first one its the joyride wrapper:
import { useEffect, useState } from 'react';
import Joyride, { CallBackProps, STATUS, Step } from 'react-joyride';
import { Tooltip } from './components/tool-tip';
import { OnboardingTourJoyrideProps, ValidSteps } from '../../types/onboarding-tour';
import { useGuidedTour } from '../../hooks/use-guided-tour';
export function OnboardingTourJoyride({ start, stepToCheck }: OnboardingTourJoyrideProps): JSX.Element {
const { dashBoardSteps, terminalAppSteps, cloudAppSteps } = useGuidedTour();
const [steps, setSteps] = useState<Step[]>(dashBoardSteps);
const [run, setRun] = useState<boolean>(false);
const [resetTour, setResetTour] = useState<boolean>(false);
const [stepToInitRestart, setStepToInitRestart] = useState<number>(0);
function handleSteps() {
switch (stepToCheck) {
case ValidSteps.DASHBOARD_STEPS:
setSteps(dashBoardSteps);
setStepToInitRestart(dashBoardSteps.length - 1);
break;
case ValidSteps.TERMINAL_APP_STEPS:
setSteps(terminalAppSteps);
setStepToInitRestart(terminalAppSteps.length - 1);
break;
case ValidSteps.CLOUD_APP_STEPS:
setSteps(cloudAppSteps);
setStepToInitRestart(cloudAppSteps.length - 1);
}
}
useEffect(() => {
const onboardinIsCompletedOrSkipped = handleCheckIfTourIsComplete();
if (!onboardinIsCompletedOrSkipped) {
handleSteps();
if (start) setRun(true);
}
}, [start]);
useEffect(() => {
if (resetTour) {
setRun(true);
setResetTour(false);
}
}, [resetTour]);
function handlePropertyToSaveOnLocalStorage() {
switch (stepToCheck) {
case ValidSteps.DASHBOARD_STEPS:
localStorage.setItem('ONBOARDING_TOUR_DASHBOARD', 'true');
break;
case ValidSteps.TERMINAL_APP_STEPS:
localStorage.setItem('ONBOARDING_TOUR_TERMINAL_APP', 'true');
break;
case ValidSteps.CLOUD_APP_STEPS:
localStorage.setItem('ONBOARDING_TOUR_CLOUD_APP', 'true');
break;
}
}
function handleCheckIfTourIsComplete() {
if (stepToCheck === ValidSteps.DASHBOARD_STEPS) {
return localStorage.getItem('ONBOARDING_TOUR_DASHBOARD');
} else if (stepToCheck === ValidSteps.TERMINAL_APP_STEPS) {
return localStorage.getItem('ONBOARDING_TOUR_TERMINAL_APP');
} else if (stepToCheck === ValidSteps.CLOUD_APP_STEPS) {
return localStorage.getItem('ONBOARDING_TOUR_CLOUD_APP');
}
}
function handleJoyrideCallback(data: CallBackProps) {
const { status, action, index } = data;
console.log(data);
const finishedStatuses: string[] = [STATUS.FINISHED, STATUS.SKIPPED];
if (index === stepToInitRestart && status === STATUS.SKIPPED) {
setRun(false);
setResetTour(true);
}
if (finishedStatuses.includes(status)) {
if (action === 'skip') {
setRun(false);
handlePropertyToSaveOnLocalStorage();
}
setRun(false);
handlePropertyToSaveOnLocalStorage();
}
}
return (
<Joyride
tooltipComponent={Tooltip}
disableScrolling={true}
callback={handleJoyrideCallback}
continuous
hideCloseButton
run={run}
scrollToFirstStep
showProgress={false}
showSkipButton={true}
steps={steps}
styles={{
options: {
zIndex: 10000,
},
}}
/>
);
}
which its a simple wrapper that contains all the logic to rehuse the same component across the app
and the other one it's the custom tooltip component:
import { Box, CloseButton, Flex, Text } from '#chakra-ui/react';
import { TooltipRenderProps } from 'react-joyride';
import { Button } from '../../button';
import { useIntl } from 'react-intl';
export function Tooltip({
backProps,
continuous,
index,
isLastStep,
primaryProps,
skipProps,
step,
tooltipProps,
size,
}: TooltipRenderProps): JSX.Element {
const intl = useIntl();
const steps = {
currentStep: index,
stepsLength: size - 2,
};
function handleResetOrBackStep(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
if (isLastStep) {
skipProps.onClick(e);
} else {
backProps.onClick(e);
}
}
function handleCloseButton(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
if (isLastStep) {
primaryProps.onClick(e);
} else {
skipProps.onClick(e);
}
}
function handleFowardButton() {
return continuous
? index !== 0 && !isLastStep
? intl.formatMessage({ id: 'buttons.next' })
: isLastStep
? intl.formatMessage({ id: 'buttons.finish' })
: intl.formatMessage({ id: 'buttons.startTour' })
: null;
}
function handleShowStepsCounter() {
return index !== 0 && !isLastStep ? (
<Text fontSize="16px">
{intl.formatMessage(
{ id: 'onboardingTour.toolTip.stepsCounter' },
{
currentStep: steps.currentStep,
totalSteps: steps.stepsLength,
},
)}
</Text>
) : (
!isLastStep && (
<Button variant="outline" {...skipProps}>
{intl.formatMessage({ id: 'buttons.skipTour' })}
</Button>
)
);
}
function handleBackwardButton() {
return isLastStep
? intl.formatMessage({ id: 'buttons.restartTour' })
: intl.formatMessage({ id: 'buttons.previous' });
}
return (
<Box {...tooltipProps} background="white" p="48px" w="100%" borderRadius="6px">
<Box padding="md">
<Flex justifyContent="space-between" align="baseline" marginBottom="15px">
{step.title && (
<Text fontSize="20px" fontWeight="700">
{step.title}
</Text>
)}
<CloseButton onClick={e => handleCloseButton(e)} />
</Flex>
{step.content && (
<Box>
<Text w="504px" fontSize="16px" fontWeight="400" lineHeight="26px" marginBottom="48px">
{step.content}
</Text>
</Box>
)}
</Box>
<Box>
<Flex justifyContent="space-between" align="center">
{handleShowStepsCounter()}
<Flex w={isLastStep ? '100%' : ''} justifyContent="space-between">
{index !== 0 && (
<Button marginRight="12px" variant="outline" onClick={e => handleResetOrBackStep(e)}>
{handleBackwardButton()}
</Button>
)}
<Button h="35px" {...primaryProps}>
{handleFowardButton()}
</Button>
</Flex>
</Flex>
</Box>
</Box>
);
}
I don't know if anyone have experience with a similar issue, I google it and search all across SO and the issues on the library repository.
Also it's weird because on the pass I worked with react-joyride and deployed to production and there is no any problem.
To put you all in context, we are using next with a framework called Gasket.
Had the same issue.
The problem is with the experimental SWC minification using the swcMinify config. It tries to optimize too much, leading to the offset variable not being used before it is declared.
It's not a problem with the version of popper.js or any other dependency. To fix the problem you just need to set swcMinify: false in your next config (also updating nextjs to 13.1.1 fixes the problem in my case).
Here is discussion about: https://github.com/gilbarbara/react-joyride/issues/857

Cannot read property 'appendChild' of null - Tiny Slider React

React newbie here, I am having problems with my TinySlider component. Each time I update in the UI how many posts can appear in the carousel, I get this error every other update which I need to fix:
Uncaught (in promise) TypeError: Cannot read property 'appendChild' of null
If I remove <TinySlider settings={...settings}></Tinyslider> I do not get this error.
I have tried this: { renderProfilesCarousel ? renderProfilesCarousel : '' } inside the <tinySlider> but that does not work.
Any idea what I could do here? Pretty stuck on this now.
// React
import * as React from 'react';
// Styling
import styles from './LinkedIn.module.scss';
// Importing the props
import { ILinkedInProps } from './ILinkedInProps';
// Importing the state
import { ILinkedInState } from './ILinkedInState';
// Removes special characters
import { escape } from '#microsoft/sp-lodash-subset';
// Library for making http requests
import axios from 'axios';
// Library for creating unique ids
import shortid from 'shortid';
// Fabric UI elements
import {
DocumentCard,
DocumentCardPreview,
DocumentCardType,
DocumentCardDetails,
DocumentCardTitle,
IDocumentCardPreviewProps
} from 'office-ui-fabric-react/lib/DocumentCard';
import { ImageFit, Image } from 'office-ui-fabric-react/lib/Image';
// Sort array
import sortBy from 'sort-array';
import TinySlider from "tiny-slider-react";
import { SPComponentLoader } from '#microsoft/sp-loader';
import "./styles.scss";
// LinkedIn Component Class
export default class LinkedIn extends React.Component<ILinkedInProps, ILinkedInState> {
// State needed for the component
constructor(props) {
super(props);
this.state = {
profiles: [],
isLoading: true,
errors: null
};
SPComponentLoader.loadCss('//cdnjs.cloudflare.com/ajax/libs/tiny-slider/2.9.2/tiny-slider.css');
}
// This function runs when component is first renderd
public componentDidMount() {
this.getProfiles();
}
// This function runs when props that have changed have been passed in
public componentDidUpdate(prevProps) {
if ( prevProps.listName !== this.props.listName || prevProps.postCount ! == this.props.postCount )
{
this.renderProfile();
}
}
// Grabs LinkedIn profiles - This service runs once a day
private getProfiles() {
let companyNameCreate: string;
let backUpImageCreate: string;
axios
.get(
"https://cors-anywhere-spfx.herokuapp.com/" +
"https://cache1.phantombooster.com/YRrbtT9qhg0/KJhwG7zo0zPE5zc9Eehn6Q/result.json"
)
.then(response => {
this.setState({
profiles: response.data.filter(d => d.postContent).map(post => {
if (post.profileUrl == 'https://www.linkedin.com/company/')
{
companyNameCreate = 'company';
backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQEbfV4VNvsJyg/company-logo_100_100/0?e=1587600000&v=beta&t=CX_s-ekYNn0TnXANeftQkLZ9jIvMW7PtDTLLcHcu9wY'
}
else if (post.profileUrl == 'https://www.linkedin.com/company/1')
{
companyNameCreate = 'company';
backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQG_Pr1cDaGfdA/company-logo_200_200/0?e=1587600000&v=beta&t=C0fWkjbO2Elth8K4pG4i_kzwlu8dvQvN1Ws-yKGxxP4'
}
else if (post.profileUrl == 'https://www.linkedin.com/company/2')
{
companyNameCreate = 'company';
backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQHdub-mnNwSNg/company-logo_100_100/0?e=1587600000&v=beta&t=druqo_O5gB5cExttREUlSdGnJhr4Wx2-PCjshJ0K0fI'
}
else if (post.profileUrl == 'https://www.linkedin.com/company/3')
{
companyNameCreate = 'company';
backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQEUKGn5i1EnQA/company-logo_100_100/0?e=1587600000&v=beta&t=uwE3CUaodiqmW2K3a3Hm57QDIDlMvrmfmoHDvlGnzuY'
}
else if (post.profileUrl =='https://www.linkedin.com/company/4')
{
companyNameCreate = 'company';
backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQGYqqxF43Rfdg/company-logo_200_200/0?e=1587600000&v=beta&t=4hmLzdbkjk_hL3rwonWgTbUF1V-itkyBEfLBh85G7_k'
}
else if (post.profileUrl == 'https://www.linkedin.com/company/5')
{
companyNameCreate = 'company';
backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4E0BAQHsNKLawvW7zg/company-logo_100_100/0?e=1587600000&v=beta&t=26Otro4T3q90GznPxXX6n3oPTYYWIgzodOIask0enu4'
}
return {
...post,
companyName: companyNameCreate,
backUpImage: backUpImageCreate
}
})
});
})
// Error catching
.catch(errors => this.setState({ errors, isLoading: false }));
}
// Creates the renderd layout of the LinkedIn profile
private async renderProfile() {
let filter: string = '';
// Works out which profile to display
switch (this.props.listName) {
case "abfi":
filter = 'https://www.linkedin.com/company/1';
break;
case 'abEnzymes':
filter = 'https://www.linkedin.com/company/2';
break;
case 'abitec':
filter = 'https://www.linkedin.com/company/3';
break;
case 'ohly':
filter = 'https://www.linkedin.com/company/4';
break;
case 'pgpi':
filter = 'https://www.linkedin.com/company/5';
break;
case 'spiPharma':
filter = 'https://www.linkedin.com/company/6';
break;
case 'all':
filter = 'Post';
break;
default:
filter = 'https://www.linkedin.com/company/1';
return filter;
}
// Grabs the array of objects
let { profiles } = this.state;
const slotOrder = [
"", "1h","2h","3h","4h","5h","6h","7h","8h","9h","10h","11h","12h", "13h","14h","15h","16h","17h","18h","19h","20h","21h","22h", "23h", "2d", "3d", "4d", "5d", "6d", "1w", "2w", "3w", "1mo", "2mo", "3mo", "4mo", "5mo", "6mo", "7mo", "8mo", "9mo", "10mo", "11mo", "1yr", "2yr"
];
// Select only the needed objects from the array
let selectedProfile;
// Display all posts
if (this.props.listName !== 'all') {
selectedProfile = profiles.filter(profile => profile.profileUrl == filter);
} else {
selectedProfile = profiles.filter(profile => profile.action == filter);
}
selectedProfile = sortBy(selectedProfile, "postDate", { postDate: slotOrder })
selectedProfile = selectedProfile.splice(0, this.props.postCount)
// Renders the selected profile
let renderProfilesCarousel = selectedProfile.map(profile => {
// If LinkedIn post has no image, then add a fallback!
if (profile.imgUrl == "" || profile.imgUrl == null ) {
profile.imgUrl = profile.backUpImage;
}
// Removes hashtag line from posts
profile.postContent = profile.postContent.replace(/hashtag/g, '');
return(
<div className={styles.linkedInContainerCarousel} style={{ position: "relative" }} key={shortid.generate()}>
<a href={profile.postUrl} target="_blank" data-interception="off">
<DocumentCard
aria-label={profile.postContent}
className={styles.linkedInDocCard}
>
{ profile.imgUrl &&
<Image
src={profile.imgUrl}
imageFit={ImageFit.cover}
height={168}
/>
}
<DocumentCardTitle
title={profile.postContent}
shouldTruncate={true}
/>
<p className={ styles.linkedInCompany}>{profile.companyName}</p>
<p className={ styles.linkedInLikes}>{`Likes: ${profile.likeCount}`}</p>
</DocumentCard>
</a>
</div>
)
});
// Renders the selected profile
let renderProfiles = selectedProfile.map(profile => {
// If LinkedIn post has no image, then add a fallback!
if (profile.imgUrl == "" || profile.imgUrl == null ) {
profile.imgUrl = profile.backUpImage;
}
let previewProps: IDocumentCardPreviewProps = {
previewImages: [
{
name: profile.postContent,
previewImageSrc: profile.imgUrl,
iconSrc: null,
imageFit: ImageFit.cover,
height: 110,
width: 110
}
]
};
return(
<div className={styles.linkedInContainer} style={{ position: "relative" }} key={shortid.generate()}>
<a href={profile.postUrl} target="_blank" data-interception="off">
<DocumentCard
aria-label={profile.postContent}
className={styles.linkedInDocCard}
type={DocumentCardType.compact}
>
{ profile.imgUrl &&
<DocumentCardPreview {...previewProps} />
}
<DocumentCardDetails>
<DocumentCardTitle
title={profile.postContent}
shouldTruncate={true}
/>
<p className={ styles.linkedInCompany}>{profile.companyName}</p>
<p className={ styles.linkedInLikes}>{`Likes: ${profile.likeCount}`}</p>
</DocumentCardDetails>
</DocumentCard>
</a>
</div>
)
});
let settings: any;
if (this.props.toggleInfoHeaderValue == true )
{
settings = {
lazyload: true,
nav: false,
mouseDrag: false,
items: 1,
swipeAngle: false,
speed: 400,
autoplay: false,
axis: "horizontal",
autoHeight: false,
rewind: true,
fixedWidth: false
};
}
else
{
settings = {
lazyload: true,
nav: false,
mouseDrag: false,
items: 3,
swipeAngle: false,
speed: 400,
autoplay: false,
axis: "vertical",
autoHeight: false,
rewind: true,
fixedWidth: false
};
};
if (this.props.toggleInfoScrollValue) {
settings.autoplay = true;
} else {
settings.autoplay = false;
}
if (this.props.toggleInfoHeaderValue == true ) {
return(
<div>
<TinySlider settings={settings}>
{renderProfilesCarousel}
</TinySlider>
</div>
)
} else {
return (
<div className={styles.upArrows}>
<TinySlider settings={settings}>
{renderProfiles}
</TinySlider>
</div>
)
}
}
// Renders to the browser
public render(): React.ReactElement<ILinkedInProps> {
return (
<div className={ styles.linkedIn }>
<div className={ styles.container }>
<p className={ styles.title }>{escape(this.props.description)}</p>
<div>
{ this.renderProfile() }
</div>
</div>
</div>
);
}
}
Error in full:
Counld you try this instead
{ renderProfilesCarousel ? renderProfilesCarousel : <span></span> }
React likes it when it has elements, and I'm not sure how it'd deal with a ''
Edit to edit:
I think you'll want to move the actual JSX.Element out of the renderProfile() method. React doesn't take that as a child element.
So I added two new items to state (I guess you'll want three, one for renderProfilesCarousel too):
settings?: any;
renderProfiles?: JSX.Element[];
Then I did this at the bottom of the renderProfile() method:
/* if (this.props.toggleInfoHeaderValue == true) {
return (
<div>
<TinySlider settings={settings}>
{renderProfilesCarousel}
</TinySlider>
</div>
)
} else {
return (
<div /* className={styles.upArrows} >
<TinySlider settings={settings}>
{renderProfiles}
</TinySlider>
</div>
)
} */
console.log(renderProfiles.length);
this.setState({
settings: settings,
renderProfiles: renderProfiles,
isLoading: false
})
Then, in your return of the actual render to the browser is where I put the actual JSX.Element:
// Renders to the browser
public render(): React.ReactElement<ILinkedInProfilesProps> {
const {settings, renderProfiles} = this.state;
const theRenderProfileJsxElement: JSX.Element =
<div /* className={styles.upArrows} */>
<TinySlider settings={settings}>
{renderProfiles}
</TinySlider>
</div>;
return (
<div /* className={styles.linkedIn} */>
<div /* className={styles.container} */>
<p /* className={styles.title} */>{escape(this.props.description)}</p>
<div>
{this.state.isLoading === false &&
theRenderProfileJsxElement
}
</div>
</div>
</div>
);
}
And I used your state of isLoading to prevent the Carousel from loading prior to finishing all the logic and loading from above.
Also! If you don't have React Dev Tools on your browser, you need it!
I can see the component Carousel, but I didn't do the if logic for either the toggleInfoHeaderValue. Let's see if that works?

Text in NavBar React-Native

I'm find a problem about a text in the navBar.
I use router-flux.
My problem is that I have insert the Logout at the right in the navBar, but I would show this button only at a category of user.
So If your role is "user1": You can logout.
If your role is "user2": You can't logout and you shouldn't see the "logout" text.
This is the code that I use to show the logout button:
App.js
export default class App extends Component {
static redirectLogout() {
Alert.alert("Logout", "Logout effettuato con successo");
Actions.authentication();
}
static logout() {
Utente.clearUtenteLoggato();
App.redirectLogout();
}
<Scene
key="homepageutente"
component={HomepageUtente}
type="reset"
leftTitle="Home"
leftButtonTextStyle={{color: "#ffffff"}}
onLeft={() => Actions.authentication()}
rightButtonTextStyle={{color: "#ffffff"}}
rightTitle="Logout"
onRight={() => App.logout()}
navigationBarStyle={{ backgroundColor: "#64c7c0" }}
/>
Do you know how can I do? If more information is needed, I will provide it immediately. Thank you very much
EDIT:
This is the structure of the App:
First page:
Authentication:
(you can go to)
--> LoginConsumer
--> LoginPlus
I Use a White page that is load after click on one button to check if you are logged in and what kind of user you are.
Starting.js
class Starting extends Component {
constructor(props) {
super(props);
this.state = {
loading: true
};
}
componentWillMount() {
Utente.getUtenteLoggato()
.then(dataUtenteLoggato => {
if (dataUtenteLoggato !== null) {
global.utente = new Utente(JSON.parse(dataUtenteLoggato));
Actions.homepageutente()({type: 'reset'});
} else if(Roles == "ROLE_PLUS") { // But this doesn't work :(
console.log(Roles)
Actions.loginplus();
}
else {
Actions.loginconsumer()
}
})
.catch(err => {
console.log(err);
})
.finally(() => {
this.setState({ loading: false });
});
}
So If you are and Role_Plus you can't logout.
Starting.js call the "getUtenteLoggato" from Utente:
static async getUtenteLoggato() {
try {
return await AsyncStorage.getItem("#UtenteLoggato");
} catch (error) {
console.log(error.message);
return null;
}
}
You could use a simple ternary condition to determine the state of this by checking on the user1 state:
<Scene
key="homepageutente"
component={HomepageUtente}
type="reset"
leftTitle="Home"
leftButtonTextStyle={{color: "#ffffff"}}
onLeft={() => Actions.authentication()}
rightButtonTextStyle={{color: "#ffffff"}}
rightTitle={user === "user1" ? "Logout" : ""}
onRight={user === "user1" ? () => App.logout() : () => {}}
navigationBarStyle={{ backgroundColor: "#64c7c0" }}
/>

Onchange event is called late as i quickly remove inputs in react

I have created this simple lorem ipsum generator, it works fine but when i input a number in it and quickly remove that number, there is still some output displayed, note that this does not happen when i remove number slowly
Generator.js
import React,{Component} from 'react';
import Output from './Output.js';
class Generator extends Component{
state = {
sentence: 0,
para: 0,
outputsentence:'',
outputpara:'',
}
sentenceHandler = async (e) => {
await this.setState({sentence: e.target.value});
if(this.state.sentence === '0' || this.state.sentence === '')
{
this.setState({outputsentence:''});
}
else{
try{
let sentence = await fetch(`https://baconipsum.com/api/?type=all-meat&sentences=${this.state.sentence}&start-with-lorem=1`);
sentence = await sentence.json();
this.setState({outputsentence:sentence});
} catch(e)
{
console.log(e);
}
}
}
paraHandler = async (e) => {
await this.setState({para: e.target.value});
if(this.state.para === '0' || this.state.para === '')
{
this.setState({outputpara:''});
}
else{
try{
console.log(e.target.value);
let paragraph = await fetch(`https://baconipsum.com/api/?type=all-meat&paras=${this.state.para}&start-with-lorem=1`);
paragraph = await paragraph.json();
this.setState({outputpara:paragraph});
} catch(e)
{
console.log(e);
}
}
}
render()
{
return(
<div className='tc'>
<h3>Number of sentences : </h3>
<input type="number" onChange={this.sentenceHandler}/>
<Output op={this.state.outputsentence} />
<h3>Number of paras : </h3>
<input type="number" onChange={this.paraHandler}/>
<Output op={this.state.outputpara} />
</div>
)
}
}
export default Generator;
Output.js
import React from 'react';
const Output = ({op}) => {
return(
<div className='tc'>
<p>
{op}
</p>
</div>
)
}
export default Output;
It works fine but suppose i enter a number 12 and quickly remove it using backspaces then still it shows 1 line or para and this does not happen if i remove it one letter that is first 2 then 1 slowly.
see it on
https://wizardly-torvalds-092022.netlify.com/
You have race conditions. Because the asynchronous fetch/display operation has no cancellation mechanism in place, nothing's going to stop the operation from completing after you start another operation (by changing the output to something else).
There are probably some good libraries to deal with this cleanly, but here's how you could go about doing it manually. You'll need the concept of a "request ID" so you know whether the currently-running operation is the latest request versus something you should cancel.
class Generator extends Component {
state = {
sentence: 0,
outputsentence: '',
currentSentenceRequestId: null,
para: 0,
outputpara:'',
currentParaRequestId: null,
}
sentenceHandler = async (e) => {
var requestId = this.state.currentSentenceRequestId++;
await this.setState({
sentence: e.target.value,
currentSentenceRequestId: requestId
});
if(this.state.sentence === '0' || this.state.sentence === '') {
this.setState({ outputsentence: '' });
} else{
try {
let sentence = await fetch(`https://baconipsum.com/api/?type=all-meat&sentences=${this.state.sentence}&start-with-lorem=1`);
sentence = await sentence.json();
// Ignore if a newer request has come in
if (this.state.currentSentenceRequestId !== requestId) {
return;
}
this.setState({
outputsentence: sentence
});
} catch(e) {
console.log(e);
} finally {
this.setState({
currentSentenceRequestId: null
});
}
}
}
// ...
}

ReactJS : callback error

I want to send my counter on my parent component but I have an error. I tried different code but none works.
I just want to send the counter and afterwards the score, if I can.
DuelTurn.JS
state = {
compteur: 0
}
constructor(props) {
super(props)
}
componentWillMount() {
this.setState({compteur: this.state.compteur++}, () => {
this.props.callback(this.state.compteur)
})
}
handleClick(step) {
if(step === true) {
console.log('gagné')
} else {
console.log('perdu')
}
}
render() {
return (
<div>
<div className="turn-player">Cest le tour de {this.props.pseudo} !</div>
<div className="text-left">
{this.props.data[0].pseudo}
<div>{this.props.firstScore}</div>
</div>
<div className="text-right">
{this.props.data[1].pseudo}
<div>{this.props.secondScore}</div>
</div>
<div className="clear"></div>
<div className="question"><div>Question :</div>La france a remporté la coupe du monde 98.</div>
<div onclick={this.handleClick(true)} className="true">Vrai</div>
<div onclick={this.handleClick(false)} className="false">Faux</div>
</div>
)
}
}
DuelGame.js
class DuelGame extends React.Component {
state = {
compteur: 0,
firstUser: this.props.dataUser[0].pseudo,
secondUser: this.props.dataUser[1].pseudo,
firstScore: 0,
secondScore: 0,
}
constructor(props) {
super(props)
}
receiveCallBack = (compteur) => {
this.setState({compteur})
console.log(compteur)
}
userTurn() {
if(this.state.compteur == 0 % 2) {
return <DuelTurn key={this.state.firstUser}
pseudo={this.state.firstUser}
firstScore={this.state.firstScore}
secondScore={this.state.secondScore}
compteur={this.state.compteur}
data={this.props.dataUser}
callback={this.receiveCallback}/>
} else {
return <DuelTurn
key={this.state.secondUser}
pseudo={this.state.secondUser}
firstScore={this.state.firstScore}
secondScore={this.state.secondScore}
compteur={this.state.compteur}
data={this.props.dataUser}
callback={this.receiveCallback}/>
}
}
render() {
return (
<div>{this.userTurn()}</div>
)
}
}
And my error is:
bundle.js:36630 Uncaught TypeError: _this2.props.callback is not a function
How to fix it?
Here is your issue, you have defined the function as receiveCallBack while you are calling this as receiveCallback (B,b).
other than that you are getting empty data in your props, I suggest you put an extra check before rendering data. So instead of this.props.dataUser[0] use this.props.dataUser && this.props.dataUser[0] and like wise for your this.props.dataUser[1] use this.props.dataUser && this.props.dataUser[1]
same goes for your this.props.data[0] and this.props.data[1] use conditional rendering there as well.
That should make it work
Here is a working version of your code
https://codesandbox.io/s/3196py0vqq

Categories