Props in functional component are missing in props validation - javascript

Eslint throwing eslint(react/prop-types) error despite already declared propTypes. I'm using eslint-plugin-react
I've looked at a couple of other similar problems and as well as the lint rule for the proptype but they don't address my issue.
import React from 'react';
import { View, Text, TouchableHighlight, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
const PASTEL_PINK = '#dea5a4';
const PASTEL_BLUE = '#779ecb';
const Buttons = ({ onPressStart, onPressPause, onPressReset, onGoing }) => (
<View >
<TouchableHighlight
onPress={onPressStart}
disabled={onGoing}
>
<Text >{START_TIMER}</Text>
</TouchableHighlight>
<TouchableHighlight
onPress={onPressPause}
disabled={!onGoing}
>
<Text >{PAUSE_TIMER}</Text>
</TouchableHighlight>
<TouchableHighlight onPress={onPressReset}>
<Text >{RESET_TIMER}</Text>
</TouchableHighlight>
</View>
);
Buttons.protoTypes = {
onPressStart: PropTypes.func.isRequired,
onPressPause: PropTypes.func.isRequired,
onPressReset: PropTypes.func.isRequired,
onGoing: PropTypes.bool.isRequired,
};
export default Buttons;
Parent component supplying the props
import React from 'react';
import Buttons from './components/Buttons'
import Container from './components/Container';
import Timer from './components/Timer';
import Inputs from './components/Inputs';
import Logo from './components/Logo';
import Buttons from './components/Buttons'
import Header from './components/Header'
export default class Home extends React.Component {
constructor(props){
super(props)
this.state = {
initialMinute: '00',
initialSecond: '00',
minute: '00',
second: '00',
completed: false,
onGoing: false,
}
componentWillMount() {
this.setState({
minute: this.state.initialMinute,
second: this.state.initialSecond,
}
);
}
componentWillUnmount() {
clearInterval(this.interval);
}
startTimer = () => {
console.log("Timer Started")
this.setState(
(prevState) => (
{
completed: false,
onGoing: true,
}
)
)
// start the timer
this.interval = setInterval(
this.decrementTime,
1000
)
}
decrementTime = () => {
if (this.state.second > 0) {
console.log(`second: ${this.state.second}`)
this.setState(
(prevState) => (
{second: prevState.second - 1}
)
)
if (this.props.second < 10) {
this.setState({
second: '0'+this.state.second
});
}
}
else {
if (this.state.minute > 0) {
this.setState(
(prevState) => (
{
minute: prevState.minute - 1,
second: prevState.second + 59,
}
)
)
if (this.props.minute < 10) {
this.setState({
state: '0'+this.state.minute
});
}
}
else {
this.resetTimer();
this.timesUp(true);
}
}
}
pauseTimer = () => {
console.log("Timer stopped")
clearInterval(this.interval);
this.setState({
onGoing: false,
}
)
}
resetTimer = () => {
console.log("Timer is reset")
this.pauseTimer();
this.setState({
minute: this.state.initialMinute,
second: this.state.initialSecond,
}
);
}
timesUp = (bool) => {
this.setState(
(prevState) => (
{
completed: bool,
}
)
)
}
optionPressed = () => {
console.log("Header is pressed")
}
handleMinuteInput = (text) => {
// clamp minute between 0 and 60
// const number = helper.clamp(parseInt(text), 0, 60)
this.setState(
{
initialMinute: text,
}
)
}
handleSecondInput = (text) => {
// const number = helper.clamp(parseInt(text+''), 0, 60)
this.setState(
{
initialSecond: text,
}
)
}
render() {
return (
<Container>
<Header onPress={this.optionPressed}/>
<Logo
slogan={'Get studying, the Pomodoro way!'}
imageSource={'../../assets/pomo-timer-logo-small.png'}
/>
<Timer
minute={this.state.minute}
second={this.state.second}
completed={this.state.completed}
onGoing={this.state.onGoing}
/>
<Buttons
onPressStart={this.startTimer}
onPressPause={this.pauseTimer}
onPressReset={this.resetTimer}
onGoing={this.state.onGoing} // true when not onGoing
/>
<Inputs
inputType={'Work'}
labelColor={PASTEL_BLUE}
handleMinuteInput={this.handleMinuteInput}
handleSecondInput={this.handleSecondInput}
onGoing={this.state.onGoing}
/>
<Inputs
inputType={'Rest'}
labelColor={PASTEL_PINK}
// setTimer={this.setTimer}
handleMinuteInput={this.handleMinuteInput}
handleSecondInput={this.handleSecondInput}
onGoing={this.state.onGoing}
/>
</Container>
)
}
}
I don't expect these error to show up but it does.
'onPressStart' is missing in props validation
'onPressPause' is missing in props validation
'onPressReset' is missing in props validation
'onGoing' is missing in props validation

Replace
Buttons.protoTypes
with
Buttons.propTypes
I have done this mistake too many times

It's propTypes, not protoTypes :)

Related

react-test-renderer test failing TypeError: Cannot read properties of undefined (reading 'current')

I'm trying to do unit tests using react-test-renderer and I can't seem to make the test pass even though the underlying component I'm testing works. Can someone tell me what I'm doing wrong
This is the code for the component
import React, { useState } from "react";
import styles from "./styles";
import { View, Pressable, Animated } from "react-native";
import Text from "components/Text/";
import { t } from "services/i18n";
import Form, { Checkbox } from "components/Form";
import WarrantyDetails from "components/WarrantyDetails";
import Icons from "icons";
import { Color } from "theme";
import animation from "./animation";
const WarrantyToggle = ({ checked = false, price = 0, onUpdate = () => { }, disabled = false }) => {
const [detailsVisible, setDetailsVisible] = useState(false);
const [isChecked, setIsChecked] = useState(checked);
const [opacity, scale] = animation(isChecked);
const onChange = (value) => {
onUpdate(value);
setIsChecked(value);
};
return (
<View style={styles.container}>
<Form
style={styles.form}
initialValues={{ warranty: checked }}>
<Checkbox
id="warranty"
name="warranty"
onChange={onChange}
disabled={disabled}
testID="boxcheck"
style={styles.checkbox}
contentStyle={styles.priceRow}
checkbox={() => (
<View style={styles.heartCheckbox}>
<Icons.Warranty size={28} color={Color.dark} strokeWidth={3} />
<Animated.View style={[styles.heart, { opacity: opacity, transform: [{ scale: scale }] }]} >
<Icons.WarrantySelected size={28} color={Color.red} strokeWidth={3} />
</Animated.View>
</View>
)}
label={() => (<Text size="3" font="bold">{t("common:add-protection-plan")}</Text>)}>
<Text style={[styles.price, { opacity: isChecked ? 1 : 0.6 }]}>{`$${price?.toFixed(2)}`}</Text>
<Pressable testID="pressdetails" size={2.5} underline onPress={() => setDetailsVisible(true)}><Text underline style={styles.details}>{t("common:details")}</Text></Pressable>
</Checkbox>
</Form>
<WarrantyDetails
testID = "warrantydetailshigher"
selected={isChecked}
setSelected={setIsChecked}
visible={detailsVisible}
onClose={() => setDetailsVisible(false)}
onChange={onChange}
price={price}
/>
</View>
);
};
export default WarrantyToggle;
This is the code for the test
import React from "react";
import renderer from "react-test-renderer";
import WarrantyToggle from "../../../src/components/WarrantyToggle";
const tree = renderer.create(<WarrantyToggle/>)
test(" Perfect lib error screen renders correctly", () => {
const tree1 = renderer
.create(
<WarrantyToggle name="hello test" />
)
.toJSON();
expect(tree1).toMatchSnapshot();
});
test('box checked' ,() =>{
const warrantycheck = tree.root.findByProps({testID:'boxcheck'})
const detailstate = tree.root.findByProps({testID:'warrantydetailshigher'})
if(detailstate.selected == true){
renderer.act(() => warrantycheck.onChange());
expect(detailstate.selected).toEqual(true)
}
else{
renderer.act(() => warrantycheck.onChange());
expect(detailstate.selected).toEqual(false)
}
});
test('press details' ,() =>{
const warrantypress = tree.root.findByProps({testID:'pressdetails'})
const detailstate = tree.root.findByProps({testID:'warrantydetailshigher'})
renderer.act(() => warrantypress.onPress());
expect(detailstate.visible).toEqual(true)
});
These are the errors I'm getting
FAIL __tests__/components/WarrantyToggle/WarrantyToggle.test.js
● Console
console.error
The above error occurred in the <WarrantyToggle> component:
at WarrantyToggle (/Users/robertbeit/Documents/sally2/app-mobile-sally-reactnative/src/components/WarrantyToggle/index.js:3777:35)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
at logCapturedError (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:10989:23)
at update.callback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11022:5)
at callCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:3662:12)
at commitUpdateQueue (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:3683:9)
at commitLifeCycles (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11855:11)
at commitLayoutEffects (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14443:7)
at HTMLUnknownElement.callCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11391:14)
at HTMLUnknownElement.callTheUserObjectsOperation (node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
● Perfect lib error screen renders correctly
TypeError: Cannot read properties of undefined (reading 'current')
at iconAnimation (src/components/WarrantyToggle/animation.js:411:83)
at WarrantyToggle (src/components/WarrantyToggle/index.js:3847:54)
at renderWithHooks (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6016:18)
at mountIndeterminateComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8744:13)
at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9966:16)
at performUnitOfWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13800:12)
at workLoopSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13728:5)
at renderRootSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13691:7)
at performSyncWorkOnRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13404:18)
at node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2537:26
at unstable_runWithPriority (node_modules/scheduler/cjs/scheduler.development.js:468:12)
at runWithPriority (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2486:10)
at flushSyncCallbackQueueImpl (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2532:9)
at flushSyncCallbackQueue (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2519:3)
at scheduleUpdateOnFiber (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13004:9)
at updateContainer (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:16454:3)
at Object.create (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:17149:3)
at Object.<anonymous> (__tests__/components/WarrantyToggle/WarrantyToggle.test.js:8:3)
I tried running yarn test but it fails.
Edit here is the code for the animation file
import { useRef, useEffect, useCallback } from "react";
import { Animated, Easing } from "react-native";
const iconAnimation = (selected) => {
const opacityValue = useRef(new Animated.Value(0)).current;
const scaleValue = useRef(new Animated.Value(0)).current;
const animateOpacityValue = useCallback(
() => {
Animated.timing(opacityValue, {
toValue: selected ? 1 : 0,
easing: Easing.inOut(Easing.quad),
duration: 300,
useNativeDriver: true,
isInteraction: false
}).start();
Animated.timing(scaleValue, {
toValue: selected ? 1.1 : 1,
easing: selected ? Easing.out(Easing.back(25)) : Easing.inOut(Easing.quad),
duration: 500,
useNativeDriver: true,
isInteraction: false
}).start();
},
[selected],
);
useEffect(() => {
animateOpacityValue();
}, [animateOpacityValue]);
return [opacityValue, scaleValue];
};
export default iconAnimation;```
this is from the index.js form file
import React, { useRef, Fragment, useEffect } from "react";
import { View } from "react-native";
import styles from "./style";
import { Formik } from "formik";
function Row({ children, getRef = () => { }, nextElement = () => { }, values, onUpdate, onPressHandler, style }) {
children = children.filter(function (element) {
return element !== undefined;
});
return (
<View style={[styles.row, style]}>
{
React.Children.map(children, (child, index) => {
if (!child || child.props === undefined) {
return <View />;
}
const id = child.props.id;
return (
<Fragment key={`row-${index}`}>
{
React.cloneElement(child, {
containerStyle: styles.rowItem,
getRef: getRef,
value: (values && values[id]) ? values[id] : "",
values: values,
flex: child.props.flex ? child.props.flex : 1,
onUpdate: onUpdate,
onPressHandler: onPressHandler ?? child.props.onPress,
nextElement: nextElement,
})
}
{
index < children.length - 1 &&
<View style={styles.gutter} />
}
</Fragment>
);
})
}
</View>
);
}
function Form({
formRef,
children,
values,
onUpdate,
initialValues,
initialTouched,
validationSchema,
onSubmitHandler,
fieldValueRef,
resetForm = false,
style = {},
gutterStyle = {}
}) {
const internalFormRef = React.createRef(undefined);
useEffect(() => {
if ((formRef && formRef.current) && resetForm) {
formRef.current.resetForm();
} else if ((internalFormRef && internalFormRef.current) && resetForm) {
internalFormRef.current.resetForm();
}
}, [resetForm, formRef, internalFormRef]);
const inputRefs = useRef([]);
const focusNextElement = (event) => {
const index = inputRefs.current.indexOf(event.target);
if (index !== -1) {
const nextElement = inputRefs.current[index + 1];
if (nextElement) {
nextElement.focus();
}
}
};
const getRef = (element, name) => {
if (element && !inputRefs?.current?.includes(element)) {
inputRefs.current.push(element);
}
};
return (
<View style={[styles.formContainer, style]}>
<Formik
innerRef={formRef ?? internalFormRef}
initialValues={initialValues}
initialTouched={initialTouched}
enableReinitialize={true}
validateOnBlur={true}
validateOnChange
validationSchema={validationSchema}
onSubmit={onSubmitHandler}
>
{({ handleSubmit, setFieldValue }) => {
if (fieldValueRef) {
fieldValueRef.current = setFieldValue;
}
return (
<>
{
React.Children.map(children, (child, index) => {
if (!child || child.props === undefined) {
return <View />;
}
return (
<Fragment>
{
React.cloneElement(child, {
nextElement: focusNextElement,
getRef: getRef,
value: (values && values[child.props.id]) ? values[child.props.id] : "",
values: values,
onUpdate: onUpdate,
onPress: child.props.willHandleSubmit ? handleSubmit : child.props.onPress
})
}
{
index < children.length - 1 &&
<View style={[styles.gutter, gutterStyle]} />
}
</Fragment>
);
})
}
</>
);
}}
</Formik>
</View>
);
}
export default Form;
export { Row };
export { default as FormValidationSchema } from "./formValidationSchemas";
export { default as Button } from "./Button";
export { default as Link } from "./Link";
export { default as NumberInput } from "./NumberInput";
export { default as LoadingIndicator } from "./LoadingIndicator";
export { default as Checkbox } from "./Checkbox";
export { default as PLPCheckbox } from "./PLPCheckbox";
export { default as Modal } from "./Modal";
export { default as RadioGroup, RadioButton } from "./RadioGroup";
export { default as SelectInput } from "./SelectInput";
export { default as TextInput } from "./TextInput";
export { default as GooglePlacesInput } from "./GooglePlacesInput";
export { default as DatePicker } from "./DatePicker";
export { default as TextInputButton } from "./TextInputButton";
I think the problem is in animation.js file. Last error line point to animation.js file. Can you share animation.js file
iconAnimation (src/components/WarrantyToggle/animation.js:411:83)

User preferred language saved but the page content language didn't change only after I reload

I'm using ant design pro.
The idea is In the platform we have 2 languages to choose from Fr(French) and En(English),
I want the user when he logs in and change the language to English for example when he logs out and log in again the language should be saved to English so he would be able to see the content in English, I managed to do it, when i login in the backend the preferedLanguage = en, the language toggle in the also changes to en, the only problem the web page content stays in French it's only change in English when i reload the page.
I think the issue is related to the login page, the login page is set to French as default , let's say my preferred language now is English, if i login from the login page. The page content loaded in French only changes when i reload.
-This is the umijs documentation : https://umijs.org/docs/max/i18n#setlocale-%E8%AE%BE%E7%BD%AE%E8%AF%AD%E8%A8%80
-LanguageDropdown (the toggle where you select the language (Fr or En)
import type { FC } from 'react';
import React, { useState, useEffect } from 'react';
import { Menu, Dropdown } from 'antd';
import { CaretDownOutlined, CaretUpOutlined, GlobalOutlined } from '#ant-design/icons';
import styles from './index.less';
import { setLocale, getLocale, getAllLocales } from 'umi';
interface Props {
setUpdateLang: any;
currentLang: string;
}
const LanguageDropdown: FC<Props> = ({ currentLang, setUpdateLang }) => {
const [langvisible, setLangVisible] = useState<boolean>(false);
const [localesList, setLocalesList] = useState<any[]>([]);
const [currentLocale, setCurrentLocale] = useState<string>('');
// useEffect(() => {
// if (currentLang) {
// setLocalesList(getAllLocales());
// setCurrentLocale(getLocale());
// setLocale(currentLang === 'fr' ? 'fr-FR' : 'en-US');
// }
// alert(currentLang);
// alert(getLocale());
// }, [currentLang]);
useEffect(() => {
setLocalesList(getAllLocales());
setCurrentLocale(getLocale());
if (currentLang) {
// alert(222);
const selectedLang = currentLang === 'fr' ? 'fr-FR' : 'en-US';
// setNewLang(selectedLang);
setLocale(selectedLang, false);
setCurrentLocale(getLocale());
}
}, [currentLang]);
const onLangVisibleChange = (visibleLang: boolean) => {
setLangVisible(visibleLang);
};
const langNameHandler = (lang: string) => {
if (lang === 'en-US') return 'EN';
else if (lang === 'fr-FR') return 'FR';
return 'FR';
};
const setNewLang = (lang: string) => {
setUpdateLang({ lang: langNameHandler(lang).toLocaleLowerCase(), updated: true });
setLocale(lang);
};
const Langmenu = (
<Menu>
{localesList?.map((lang: any) => (
<Menu.Item key={lang}>
<a onClick={() => setNewLang(lang)}>{langNameHandler(lang)}</a>
</Menu.Item>
))}
</Menu>
);
return (
<div className={styles.profileDropdownContainer}>
<Dropdown
overlay={Langmenu}
placement="bottomLeft"
trigger={['click']}
onVisibleChange={onLangVisibleChange}
className={styles.dropdown}
>
<div className={styles.langContainer}>
<span>
<GlobalOutlined /> {langNameHandler(currentLocale)}
</span>
{!langvisible ? <CaretDownOutlined /> : <CaretUpOutlined />}
</div>
</Dropdown>
</div>
);
};
export default LanguageDropdown;
-RightContext
import { Tag } from 'antd';
import type { Settings as ProSettings } from '#ant-design/pro-layout';
import React, { useEffect, useState } from 'react';
import type { ConnectProps } from 'umi';
import type { Dispatch } from 'umi';
import { connect } from 'umi';
import type { ConnectState } from '#/models/connect';
import Avatar from './AvatarDropdown';
import styles from './index.less';
import LanguageDropdown from '../languageDropdown';
import moment from 'moment';
export type GlobalHeaderRightProps = {
dispatch: Dispatch;
theme?: ProSettings['navTheme'] | 'realDark';
auth: any;
users: any;
platformLanguage: any;
data: any;
} & Partial<ConnectProps> &
Partial<ProSettings>;
const ENVTagColor = {
dev: 'orange',
test: 'green',
pre: '#87d068',
};
const GlobalHeaderRight: React.FC<GlobalHeaderRightProps> = (props) => {
const [updateLang, setUpdateLang] = useState<{ lang: string; updated: boolean }>({
lang: '',
updated: false,
});
const [currentLang, setCurrentLang] = useState<any>(null);
const { theme, layout, auth, platformLanguage, data, dispatch } = props;
let className = styles.right;
useEffect(() => setCurrentLang(platformLanguage), [platformLanguage]);
useEffect(() => {
if (updateLang.updated) {
const {
organization,
roles,
email,
deleted,
department,
createdById,
organizationId,
...rest
} = data;
const birthdate = moment(rest.birthdate).format('YYYY-MM-DD');
const workversary = moment(rest.workversary).format('YYYY-MM-DD');
dispatch({
type: 'users/updateMe',
payload: {
data: { ...rest, birthdate, workversary, platformLanguage: updateLang.lang },
userId: auth?.currentUser.id,
},
});
setUpdateLang({ ...updateLang, updated: false });
setCurrentLang(updateLang.lang);
}
}, [updateLang]);
if (theme === 'dark' && layout === 'top') {
className = `${styles.right} ${styles.dark}`;
}
return (
<div className={className}>
{currentLang ? (
<LanguageDropdown currentLang={currentLang} setUpdateLang={setUpdateLang} />
) : null}
<Avatar />
{REACT_APP_ENV && (
<span>
<Tag color={ENVTagColor[REACT_APP_ENV]}>{REACT_APP_ENV}</Tag>
</span>
)}
</div>
);
};
export default connect(({ settings, auth, users }: ConnectState) => ({
theme: settings.navTheme,
layout: settings.layout,
auth,
users,
platformLanguage: auth?.currentUser?.membership?.platformLanguage,
data: auth?.currentUser?.membership,
}))(GlobalHeaderRight);
-LoginLayout
import React, { useEffect, useState } from 'react';
import type { ConnectState } from '#/models/connect';
import type { MenuDataItem } from '#ant-design/pro-layout';
import { getMenuData, getPageTitle } from '#ant-design/pro-layout';
import { useIntl, connect } from 'umi';
import type { ConnectProps } from 'umi';
import { Col, Row } from 'antd';
import { Helmet, HelmetProvider } from 'react-helmet-async';
import LoginImage from '../assets/loginImage.png';
import SignUpAdminImage from '../assets/adminSignup.svg';
import styles from './LoginLayout.less';
import LanguageDropdown from '#/components/languageDropdown';
import SignupSideText from '#/components/SignupSideText';
export type UserLayoutProps = {
breadcrumbNameMap: Record<string, MenuDataItem>;
} & Partial<ConnectProps>;
const LoginLayout: React.FC<UserLayoutProps> = (props) => {
const [layoutImage, setLayoutImage] = useState(LoginImage);
const [updateLang, setUpdateLang] = useState<{ lang: string; updated: boolean }>({
lang: '',
updated: false,
});
const [currentLang, setCurrentLang] = useState<any>('');
useEffect(() => {
if (updateLang.updated) {
setUpdateLang({ ...updateLang, updated: false });
setCurrentLang(updateLang.lang);
}
}, [updateLang]);
useEffect(() => {
if (window.location.pathname === '/user/adminSignup/step1') {
setLayoutImage(SignUpAdminImage);
} else if (window.location.pathname === '/user/adminSignup/step2') {
setLayoutImage('TextSignup');
} else setLayoutImage(LoginImage);
}, [window.location.pathname]);
const {
route = {
routes: [],
},
} = props;
const { routes = [] } = route;
const {
children,
location = {
pathname: '',
},
} = props;
const { formatMessage } = useIntl();
const { breadcrumb } = getMenuData(routes);
const title = getPageTitle({
pathname: location.pathname,
formatMessage,
breadcrumb,
...props,
});
return (
<>
<HelmetProvider>
<Helmet>
<title>{title}</title>
<meta name="description" content={title} />
</Helmet>
<div className={styles.container}>
<Col
xl={{ span: 12, order: 1 }}
xs={{ span: 0, order: 2 }}
md={{ span: 0, order: 2 }}
lg={{ span: 0, order: 2 }}
style={{ backgroundColor: '#00bfa5' }}
>
{layoutImage === 'TextSignup' ? (
<SignupSideText />
) : (
<img alt="logo" width="100%" height="100%" src={layoutImage} />
)}
</Col>
<Col
xl={{ span: 12, order: 2 }}
lg={{ span: 24, order: 2 }}
sm={{ span: 24, order: 1 }}
xs={{ span: 24, order: 1 }}
>
{' '}
<Row justify="end" className="languageRow">
<LanguageDropdown currentLang={currentLang} setUpdateLang={setUpdateLang} />
</Row>
{children}
</Col>
</div>
</HelmetProvider>
</>
);
};
export default connect(({ settings }: ConnectState) => ({ ...settings }))(LoginLayout);

Issue tracking index value for react-images using React, Gatsby and GraphQL

Description:
Currently trying to integrate react-images into a project using React and Gatsby. Done a lot of tinkering with different possible fixes, and I am unable to locate the issue. It seems like the issue either lies within mapping the images or tracking the currentIndex value properly.
ContainerLightBox:
import React from 'react'
import { StaticQuery, graphql } from "gatsby"
import LightBox from './LightBox'
const ContainerLightBox = () => (
<StaticQuery
query={graphql`
query {
images: allFile(
filter: { sourceInstanceName: { eq: "gallery" } }
sort: { fields: name, order: ASC }
) {
edges {
node {
childImageSharp {
fluid(maxWidth: 1000) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
`}
render={data => <LightBox images={data.images.edges} />}
/>
)
export default ContainerLightBox
LightBox:
import React, { Component, Fragment } from 'react'
import Img from 'gatsby-image'
import Lightbox from 'react-images'
export default class LightBox extends Component {
constructor(props) {
super(props)
this.state = {
lightboxIsOpen: false,
currentImage: 0
}
}
toggleLightbox = ( i, e ) => {
e.preventDefault()
this.setState(
{
lightboxIsOpen: !this.state.lightboxIsOpen,
},
() => {
this.state.active
? this.setState({
currentImage: i,
})
: this.setState({
currentImage: 0,
})
}
)
}
gotoPrevious = () => {
this.setState({
currentImage: this.state.currentImage - 1
})
}
gotoNext = () => {
this.setState({
currentImage: this.state.currentImage + 1
})
}
handleClickImage = () => {
if (this.state.currentImage === this.props.images.length - 1) {
return this.gotoNext()
}
}
render() {
const { images } = this.props
return (
<Fragment>
{images.map(( photo, i ) => {
return (
<a
key={i}
href={photo.node.childImageSharp.fluid.src}
onClick={e => this.toggleLightbox( i, e )}
className="photo-container"
>
<Img
fluid={photo.node.childImageSharp.fluid}
style={{ width: '100%', height: '100%' }}
/>
</a>
)
})}
<Lightbox
images={images.map( photo => ({
src: photo.node.childImageSharp.fluid.src,
srcSet: photo.node.childImageSharp.fluid.srcSet
}))}
currentImage={this.state.currentImage}
isOpen={this.state.lightboxIsOpen}
onClickPrev={this.gotoPrevious}
onClickNext={this.gotoNext}
onClickImage={this.handleClickImage}
onClose={this.toggleLightbox}
showImageCount={false}
backdropClosesModal={true}
/>
</Fragment>
)
}
}
Error:
TypeError: Cannot read property '0' of undefined
Carousel.getViewData
1574 | var currentIndex = _this2.state.currentIndex;
1575 |
1576 |
1577 | return views[currentIndex];
1578 | };
1579 |
1580 | this.focusViewFrame = function () {

React Native: Component rerender but props has not changed

I'm encountering this strange issue that I can figure out why is happing.
This should not be happening since the prop passed down to the History component has not been updated.
./components/History.js
...
const History = ({ previousLevels }) => {
return (
<ScrollView style={styles.container}>
{previousLevels.reverse().map(({ date, stressValue, tirednessValue }) => {
return (
<CardKBT
key={date}
date={date}
stressValue={stressValue}
tirednessValue={tirednessValue}
/>
)
})}
</ScrollView>
)
}
...
export default History
As can be seen in this code (below), the prop to the History is only updated once the user press Save.
App.js
import React from 'react'
import { View, ScrollView, StyleSheet } from 'react-native'
import { AppLoading, Font } from 'expo'
import Store from 'react-native-simple-store'
import { debounce } from 'lodash'
import CurrentLevels from './components/CurrentLevels'
import History from './components/History'
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
isLoadingComplete: false,
currentLevels: {
stressValue: 1,
tirednessValue: 1,
},
previousLevels: [],
}
this.debounceUpdateStressValue = debounce(this.onChangeStressValue, 50)
this.debounceUpdateTirednessValue = debounce(
this.onChangeTirednessValue,
50
)
}
async componentDidMount() {
const previousLevels = await Store.get('previousLevels')
if (previousLevels) {
this.setState({ previousLevels })
}
}
render() {
const { stressValue, tirednessValue } = this.state.currentLevels
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return (
<AppLoading
...
/>
)
} else {
return (
<View style={{ flex: 1 }}>
<CurrentLevels
stressValue={stressValue}
onChangeStressValue={this.debounceUpdateStressValue}
tirednessValue={tirednessValue}
onChangeTirednessValue={this.debounceUpdateTirednessValue}
onSave={this.onSave}
/>
<History previousLevels={this.state.previousLevels} />
</View>
)
}
}
...
onChangeStressValue = stressValue => {
const { tirednessValue } = this.state.currentLevels
this.setState({ currentLevels: { stressValue, tirednessValue } })
}
onChangeTirednessValue = tirednessValue => {
const { stressValue } = this.state.currentLevels
this.setState({ currentLevels: { stressValue, tirednessValue } })
}
onSave = () => {
Store.push('previousLevels', {
date: `${new Date()}`,
...this.state.currentLevels,
}).then(() => {
Store.get('previousLevels').then(previousLevels => {
this.setState({
currentLevels: { stressValue: 1, tirednessValue: 1 },
previousLevels,
})
})
})
}
}
The component will re-render when one of the props or state changes, try using PureComponent or implement shouldComponentUpdate() and handle decide when to re-render.
Keep in mind, PureComponent does shallow object comparison, which means, if your props have nested object structure. It won't work as expected. So your component will re-render if the nested property changes.
In that case, you can have a normal Component and implement the shouldComponentUpdate() where you can tell React to re-render based on comparing the nested properties changes.

How to create shared-singleton components across all the platform?

I'm thinking on creating a React component called LoadingMask, where I can show or not (depending on the state) a loading mask from any component. The idea is showing it before an ajax call, and hiding it after I receive the data.
I don't want to display two masks at the same time, so if one component is making a request, and another one creates another request, I want to add 1 to my "MaskCounter", and substract one when the Request is finished. If the counter is 0, I need to hide the LoadingMask.
I order to do this, I think I need to create a "Singleton" component, that I can share through the whole platform, so there's only exist one LoadingMask. I also don't think it's nice to send the events to hide/show the mask to all components.
Any ideas?
To share data between components, you can :
Use a lib like Redux, and keep in shared store your mask loader status
Use the React context api from your root component, and share loader status to all childrens. See an example below :
class Application extends React.Component {
constructor() {
super();
this.state = {
nbTasks: 0
};
this.addTask = this.addTask.bind(this);
this.removeTask = this.removeTask.bind(this);
this.isLoading = this.isLoading.bind(this);
}
addTask() {
this.setState(prevState => ({
nbTasks: prevState.nbTasks + 1
}));
}
removeTask() {
this.setState(prevState => ({
nbTasks: prevState.nbTasks - 1
}));
}
isLoading() {
return this.state.nbTasks > 0;
}
getChildContext() {
return {
addTask: this.addTask,
removeTask: this.removeTask,
isLoading: this.isLoading
};
}
render() {
return (
<div>
<ComponentX />
<ComponentY />
<LoadingMask />
</div>
);
}
}
Application.childContextTypes = {
addTask: PropTypes.func,
removeTask: PropTypes.func,
isLoading: PropTypes.func
};
const LoadingMask = (props, context) => (
context.isLoading()
? <div>LOADING ...</div>
: null
);
LoadingMask.contextTypes = {
isLoading: PropTypes.func
};
class ComponentX extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
message: 'Processing ...'
};
}
componentDidMount() {
this.context.addTask();
setTimeout(() => {
this.setState({
message: 'ComponentX ready !'
});
this.context.removeTask();
}, 3500);
}
render() {
return (
<div>
<button disabled>{this.state.message}</button>
</div>
);
}
}
ComponentX.contextTypes = {
addTask: PropTypes.func,
removeTask: PropTypes.func
};
class ComponentY extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
message: 'Processing ...'
};
}
componentDidMount() {
this.context.addTask();
setTimeout(() => {
this.setState({
message: 'ComponentY ready !'
});
this.context.removeTask();
}, 6000);
}
render() {
return (
<div>
<button disabled>{this.state.message}</button>
</div>
);
}
}
ComponentY.contextTypes = {
addTask: PropTypes.func,
removeTask: PropTypes.func
};
ReactDOM.render(
<Application />,
document.getElementById('app')
);
<script src="https://unpkg.com/prop-types/prop-types.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js"></script>
<div id="app"></app>
I found this library use-between to be simple, powerful and useful. It removes complexity of redux for sharing data between within functional components.
import React, { useState, useCallback } from 'react';
import { useBetween } from 'use-between';
Context/Session.ts
export const useShareableState = () => {
const [count, setCount] = useState(0);
const inc = useCallback(() => setCount(c => c + 1), []);
const dec = useCallback(() => setCount(c => c - 1), []);
return {
count,
inc,
dec
};
};
App.tsx
import { useBetween } from 'use-between';
import { useShareableState } from './src/Context/Session'
const useSharedCounter = () => useBetween(useShareableState);
const Count = () => {
const { count } = useSharedCounter();
return <p>{count}</p>;
};
const Buttons = () => {
const { inc, dec } = useSharedCounter();
return (
<>
<button onClick={inc}>+</button>
<button onClick={dec}>-</button>
</>
);
};
const App = () => (
<>
<Count />
<Buttons />
<Count />
<Buttons />
</>
);
export default App;

Categories

Resources