Whole component re renders when modal opens in react - javascript

<ThemeProvider theme={theme}>
<GlobalStyle />
{componentName !== 'questionaire' &&
componentName !== 'activityResult' && <CardWrapper />}
<ErrorModal
errorModal={errorModal}
handleErrorModal={handleError}
errorMsg={error}
/>
{successModal && successMsg ? (
<SuccessModal successModal={successModal} successMsg={successMsg} />
) : (
<Grid
className="card-layout"
style={
componentName === 'questionaire'
? { margin: '20px', height: 'calc(100% - 40px)' }
: { margin: '30px 20px' }
}
>
{customTagProps.meterId && (
<CustomTag type={componentName} propData={customTagProps} />
)}
</Grid>
)}
</ThemeProvider>
I have a modal component, which on load re renders the entire component. I want to prevent the re render of the entire component.

lack of Information
may I only suggest you try using useRef instead of useState
you could use useMemo() to return the component

Related

react recursive accordion component throwing an error each child in a list should have a unique key prop

I'm writing custom nested recursive accordion component to display objects, In that component I'm getting an error each child in a list should have a unique key prop, not sure where I need to put the key property
how to resolve the same.
accordion.js:
import React, { useState, useCallback } from 'react'
import { Container, Grid } from '#material-ui/core'
import './styles.css'
function Accodian({ explorer }) {
const [ expand, setExpand ] = useState(false)
const handleExpand = useCallback(() => {
setExpand(prevState => !prevState)
})
if (explorer.children) {
return (
<div style={{ width: '100%' }} className={'tabs'} key={explorer.label}>
{explorer.children ? (
<>
<div className='tab' onClick={handleExpand}>
<label className={ expand ? 'tab-label-expanded' : 'tab-label' }>
{explorer.label}
</label>
</div>
{expand ? (
<Container className='tab-content'>
<Grid container spacing={1}>
{explorer.children.map(child => {
return (
<React.Fragment>
{Array.isArray(child.children) ? (
<Accodian explorer={child}/>
) : (
<Grid item xs={12} sm={6} md={6} lg={4} xl={3}>
<li>
<b>{child.label}</b>{' '}: {child.value}
</li>
</Grid>
)}
</React.Fragment>
)
})}
</Grid>
</Container>
) : null}
</>
) : null}
</div>
)
} else {
return <div style={{ paddingLeft: '20px' }}>{explorer.label}</div>
}
}
export default Accodian
After did many analysis found the place where we can able to apply the key in the component
Solution: <React.Fragment key={child.label}>
import React, { useState, useCallback } from 'react'
import { Container, Grid } from '#material-ui/core'
import './styles.css'
function Accodian({ explorer }) {
const [ expand, setExpand ] = useState(false)
const handleExpand = useCallback(() => {
setExpand(prevState => !prevState)
})
if (explorer.children) {
return (
<div style={{ width: '100%' }} className={'tabs'} key={explorer.label}>
{explorer.children ? (
<>
<div className='tab' onClick={handleExpand}>
<label className={ expand ? 'tab-label-expanded' : 'tab-label' }>
{explorer.label}
</label>
</div>
{expand ? (
<Container className='tab-content'>
<Grid container spacing={1}>
{explorer.children.map(child => {
return (
<React.Fragment key={child.label}> // applied key
{Array.isArray(child.children) ? (
<Accodian explorer={child}/>
) : (
<Grid item xs={12} sm={6} md={6} lg={4} xl={3}>
<li>
<b>{child.label}</b>{' '}: {child.value}
</li>
</Grid>
)}
</React.Fragment>
)
})}
</Grid>
</Container>
) : null}
</>
) : null}
</div>
)
} else {
return <div style={{ paddingLeft: '20px' }}>{explorer.label}</div>
}
}
export default Accodian

How to simply nested ternary logic in react while rendering different component

I have the following logic inside a react component where, I am rendering different component based on the boolean values. This peace of code is very difficult to understand. Are there anyways, I can simply that logic:
{isEnabled ? (
<>
{!loading ? (
<>
{items.length === 0 ? (
<>
<ComponentOne/>
<Container>
<img src={Image} alt="Image" />
</Container>
</>
) : (
<ComponentTwo/>
)}
</>
) : (
<div>
<LoadingComponent/>
</div>
)}
</>
) : (
<ComponentThree/>
)}
I'd probably split it up into seperate components and pass parameters down the component tree for example
{isEnabled ? <IsLoadingComponent loading={loading} items={items}> : <ComponentThree/>}
You might find it useful to split the component up into a "Loading" version and a "Loaded" version so you don't have to handle both states in the same component. Then the component basically just renders the "Loading" or "Loaded" version depending on the flag.
But even without that, you can at least make that easier to debug by using if/else if etc. and assigning to a temporary variable:
let comp;
if (isEnabled) {
if (loading) {
comp = <div>
<LoadingComponent/>
</div>;
} else if (items.length === 0) {
comp = <>
<ComponentOne/>
<Container>
<img src={Image} alt="Image" />
</Container>
</>;
} else {
comp = <ComponentTwo />;
}
} else {
comp = <ComponentThree />;
}
Then just
{comp}
where that nested conditional was.
I think you are making a simple thing very complicated. What we can do instead is that make use of "&&".
{ isEnabled && loading && <LoaderComponent /> }
{isEnabled && !items.length &&
<>
<ComponentOne/>
<Container>
<img src={Image} alt="Image" />
</Container>
</>
}
{isEnabled && items.length && <ComponentTwo/>}
{!isEnabled && <ComponentThree />}
Though I want to support the argument the others made (split into multiple components), you can already achieve a bit more readability by dropping unnecessary fragments (<></>) and/or parenthesis and by using "better"(opinion) indentation.
return (
isEnabled
? loading
? <div><LoadingComponent/></div>
: items.length === 0
? <> {/* this is the only place a fragment is actually needed */}
<ComponentOne/>
<Container>
<img src={Image} alt="Image"/>
</Container>
</>
: <ComponentTwo/>
: <ComponentThree/>
);
Alternatively, early returns do help a lot with readability. For example:
const SomeComponent = () => {
// ...snip...
if (!isEnabled) {
return <ComponentThree/>;
}
if (loading) {
return <div><LoadingComponent/></div>;
}
if (items.length > 0) {
return <ComponentThree/>;
}
return (
<>
<ComponentOne/>
<Container>
<img src={Image} alt="Image"/>
</Container>
</>
);
}

Passing props to children in React

I'm trying to make a Formik wrapper which takes children as props and would render anything put inside. There are a couple forms to make which take different initial values and validation schema etc. The only thing in common thing is the grid layout. The goal is to have the access to Formik props like values, errors etc. in the child component and I have no idea how to pass it to its child. The form fields don't even show up.
The wrapper:
import React from 'react';
import { Formik, FormikConfig, FormikValues } from "formik";
import { Col, Layout, Row } from "antd";
const FormContainer: React.FC<FormikConfig<FormikValues>> = ({ children, ...props }) => {
return <Formik
{...props}
>
{props => (
<Layout>
<Row style={{ height: "100vh", display: "flex", alignItems: "center" }}>
<Col span={12}>
<Layout>
{/*this will be replaced with some background image*/}
<pre>{JSON.stringify(props.values, null, 2)}</pre>
<pre>{JSON.stringify(props.errors, null, 2)}</pre>
</Layout>
</Col>
<Col span={12}>
<Layout>
{/*here goes goes a Form from a different components*/}
{children}
</Layout>
</Col>
</Row>
</Layout>
)}
</Formik>
};
export default FormContainer;
I must be doing something wrong. I am unable to get any Formik props/values from anywhere else when I wrap FormContainer around anything.
My form example (so far):
import React from "react";
import { Field, Form } from "formik";
import { Col, Form as AntForm, Icon, Input, Row } from "antd";
import { initialValues, validationSchema } from "./fieldValidation";
import FormContainer from "../../../containers/FormContainer/FormContainer";
const RegisterPage: React.FC = () => {
return (
<FormContainer
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(data, { setSubmitting }) => {
setSubmitting(true);
setTimeout(() => {
alert(JSON.stringify(data, null, 2));
setSubmitting(false);
}, 5000);
}}
>
{({touched, errors}) => (
<Form>
<Row gutter={[8, 8]}>
<Col span={12}>
<AntForm.Item
help={touched.firstName && errors.firstName ? errors.firstName : ""}
validateStatus={touched.firstName && errors.firstName ? "error" : undefined}
>
<Field
name="firstName"
prefix={<Icon type="solution" style={{ color: "rgba(0,0,0,.25)" }} />}
placeholder="First name"
as={Input}
/>
</AntForm.Item>
</Col>
<Col span={12}>
<AntForm.Item
help={touched.lastName && errors.lastName ? errors.lastName : ""}
validateStatus={touched.lastName && errors.lastName ? "error" : undefined}
>
<Field
name="lastName"
prefix={<Icon type="solution" style={{ color: "rgba(0,0,0,.25)" }} />}
placeholder="Last name"
as={Input}
/>
</AntForm.Item>
</Col>
</Row>
</Form>
)}
</FormContainer>
);
};
export default RegisterPage;
I'm stuck. What am I doing wrong here?
Here's how to pass the prop "propsToPass" from the parent to all his direct children:
const Parent = props => {
const { children } = props;
const childrenWithExtraProp = React.Children.map(children, child =>
React.cloneElement(child, { propsToPass: "toChildren" })
);
return <div>{childrenWithExtraProp}</div>;
};
export default Parent;
So in this case, both children will have the prop "propsToPass"
<Parent>
{/* this.props.propsToPass will be available in this component */}
<Child></Child>
{/* this.props.propsToPass will be available in this component */}
<AnotherChild></AnotherChild>
</Parent>
You could do the same for your form.
I don't see like rendering Formik as children is good idea here, especially that you are supposed to render one form in such FormWrapper. I would use render props here, so here is basic example how you can do it.
Anyway, I still can't get your concept of re-inventing FormWrapper if Formik provides its own wrapper:
https://jaredpalmer.com/formik/docs/api/formik
interface FormWrapperProps extends FormikConfig<FormikValues> {
renderForm(props: FormWrapperProps): React.ReactNode
}
export const RegisterForm = (props: FormWrapperProps) => (
<form>
<input type="text"/>
<input type="text"/>
</form>
)
const FormWrapper: React.FC<FormWrapperProps> = (props) => {
return (
<div className="layout">
{/*here goes goes a Form from a different components*/}
{props.renderForm(props)}
</div>
)
}
const FormPage = () => {
const props = {} as FormWrapperProps
return (
<FormWrapper
{...props}
renderForm={(props: FormWrapperProps) => <RegisterForm {...props} />}
/>
)
}

better jsx condition to order component

I have an icon position prop that decide whether it's placed on the left or on the right of the children. I have this working
<List>
{iconPosition === 'right' && (
<Text />
)}
{icon && (
<Icon />
)}
{iconPosition === 'left' && (
<Text />
)}
</List>
But I think it can be more simple, although in my opinion my above code is readable.
You can't do this any other way. You're using individual conditions with their own result.
Maybe you could place them on one line to make them more readable.
<List>
{iconPosition === 'right' && <Text />}
{icon && <Icon />}
{iconPosition === 'left' && <Text />}
</List>
or
render() {
const textLeft = (iconPosition === 'left');
const textRight = (iconPosition === 'right');
...
}
And use that. But that's mostly down to your preference / the coding style you and your colleagues are using..
One way to do it is to use css float (left/right) but it'll make the icon to be all the way to the right. Another way is to use flex box, take for example:
<div style="display: flex; flex-direction: row;">
<div style="flex: 0; order: 1;">(X)</div>
<div>content</div>
</div>
The (X) is the "icon". The order: 1; in the style moves it to the right, if you change it to 0 or ommit it, it'll be on the left.
so if you use this approach and put the text + icon in a flex row, all you need to do is:
<Icon style={{order: (iconPosition === 'right' ? 1 : 0)}} />
You could introduce a higher-order component to attempt to deal with it. It does add some complexity to the project though.
const WithConditionalRender = WrappedComponent => (
({ renderCondition, ...props }) => {
if (renderCondition) {
return <WrappedComponent { ...props } />;
}
return null;
}
);
const ConditionalText = WithConditionalRender(Text);
const ConditionalIcon = WithConditionalRender(Icon);
const Example = ({ icon, iconPosition }) => (
<List>
<ConditionalText renderCondition={ iconPosition === 'right' } />
<ConditionalIcon renderCondition={ icon } />
<ConditionalText renderCondition={ iconPosition === 'left' } />
</List>
);
This is a generic HOC and could be tweaked to be more specific.

Material-UI BottomNavigationItem URL

UI on a react component. I have a <BottomNavigationItem /> component. This actually renders as an <button>. How can I actually make it render/navigate to a URL?
class FooterNavigation extends Component {
state = {
selectedIndex: 0,
};
select = (index) => this.setState({selectedIndex: index});
render() {
return (
<footer className="mdl-mini-footer">
<Paper zDepth={1}>
<BottomNavigation selectedIndex={this.state.selectedIndex}>
<BottomNavigationItem
label="Reviews"
icon={reviewIcon}
onClick={() => this.select(0)}
/>
</BottomNavigation>
</Paper>
</footer>
);
}
}
Simply you can just add containerElement={<Link to="/home"/>} don't forget to import Link from react-router-dom
So it will be like this:
<BottomNavigationItem
containerElement={<Link to="/home"/>}
label="Reviews"
icon={reviewIcon}
onClick={() => this.select(0)}
/>

Categories