React conditional components mount multiple times - reactjs

I'm not using react router and simply have some condition to show and hide certain components.
return (<div className={classes.content}>
{ Navigation[this.state.view] && Navigation[this.state.view].selected === 'colors' && <Colors /> }
{ Navigation[this.state.view] && Navigation[this.state.view].selected === 'typography' && <Typography /> }
{ Navigation[this.state.view] && Navigation[this.state.view].selected === 'decoration' && <Decoration /> }
{ Navigation[this.state.view] && Navigation[this.state.view].selected === 'footer' && <Footer /> }
</div>)
This will call componentDidMount inside each component with the number of times that those conditions are met. Can you pinpoint what's wrong with the way I'm showing and hiding the components.

One way to solve your problem could be to store the map to your pages in your class or in any variable outside of your render. The component will need to be wrapped into functions to not be mounted when initializing your variable.
This can be done in your constructor for example :
this.pages = {
colors: () => <Colors/>,
typography: () => <Typography/>,
decoration: () => <Decoration/>,
footer: () => <Footer/>
}
Now, in your render, just take out the navigation page name you wish to load, extract the right one from your JSON object and call the function to render the correct element without loading the others :
const nav = Navigation[this.state.view]
return (
<div className={classes.content}>
{nav && this.pages[nav.selected]()}
</div>
)

I think it's because of that you are creating a react element every time those conditions are met. So I believe you can fix that by creating those components once, and returning them as needed.
So you need something like this:
class Something extends Component {
constructor() {
this.pages = {
colors: <Colors />,
typography: <Typography />
}
}
render() {
return (
<div className={classes.content}>
{ Navigation[this.state.view] && Navigation[this.state.view].selected === 'colors' && this.pages.colors }
{ Navigation[this.state.view] && Navigation[this.state.view].selected === 'typography' && this.pages.typography }
</div>
)
}
}
(You can also implement some lazy-loading behavior to createElements as needed and returning the previously created one if is available)

Related

How to add conditional statement inside react fragment

Inside of the react fragment I have to add conditional statement. On basis of the conditional statement, return what expected
return (
<React.Fragment>
<Toolbar
pageTitle={i18next.t('TITLE')}
iconButtons={this.state.icons}
{this.props.abc && this.props.abc.operation ?(
moreButton={moreButton}
):null}
/>
if this.props.abc.operation is present then only show morebutton if not show only iconbuttons this is my condition and above is the code i tried. any help would be really appreciated.
<>
<Toolbar
pageTitle={i18next.t('TITLE')}
iconButtons={this.state.icons}
moreButton={this.props.abc && this.props.abc.operation && moreButton}
/>
</>
Try to use this.
Instead of conditional rendering you can do like below.
isAbcOperationExist = (args) => {
if(args && args.operation){
return true;
}
return false;
}
Now inside component props:
<Toolbar
pageTitle={i18next.t('TITLE')}
iconButtons={this.state.icons}
showMoreButton={() => this.isAbcOperationExist(this.props.abc)}
/>
Based on result returned boolean value by method isAbcOperationExist you can show or hide moreButton
More Example:
Assumption this is class based component:
class YourComponent extends React.Component {
constructor(props){
super(props)
}
isAbcOperationExist = (args) => {
if(args && args.operation) {
return true;
}
return false;
}
render (){
return (
<Toolbar
pageTitle={i18next.t('TITLE')}
iconButtons={this.state.icons}
moreButton={moreButton}
showMoreButton={() => this.isAbcOperationExist(this.props.abc)}
/>
)
}
}
For Toolbar Component assuming it as functional base component:
const Toolbar = ({pageTitle, iconButtons, showMoreButton, moreButton}) => {
return(
<div>
{
showMoreButton ? <button onClick={moreButton}>Show More</button> : null
}
</div>
)
}
React Fragment has nothing to do with this. You also can't manipulate component props like this. The idea would be to have a single prop for iconButtons and moreButton and and do the logic what to show inside Toolbar component

Rendered fewer hooks than expected. This may be caused by an accidental early return statement. Conditional rendering component based on state [duplicate]

This question already has an answer here:
React hooks: call component as function vs render as element
(1 answer)
Closed 1 year ago.
I'm new to React Hooks, I'm facing this error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement. when I tried to do a conditional rendering in my function component.
Here's my component:
export const MyComponent= () => {
const [page, SetPage] = useState("");
return (
<div>
{
page === "" && <>
<div onClick={() => SetPage("Page1")}>Show Page 1</div>
<div onClick={() => SetPage("Page2")}>Show Page 2</div>
<div onClick={() => SetPage("Page3")}>Show Page 3</div>
</>
}
{
page === "Page1" && Page1()
}
{
page === "Page2" && Page2()
}
{
page === "Page3" && Page3()
}
</div>
);
};
When I click on "Show Page 1", I expect it to display the Page1() functional component. However I get this error instead: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.
I'm sure it's a stupid beginner mistake, and I tried to look through similar errors and solutions online but no luck. Any help is appreciated.
Assuming Page1, Page2 and Page3 are React components, instead of:
{
page === "Page1" && Page1()
}
{
page === "Page2" && Page2()
}
{
page === "Page3" && Page3()
}
Try:
{
page === "Page1" && <Page1 />
}
{
page === "Page2" && <Page2 />
}
{
page === "Page3" && <Page3 />
}

useState function passed down through props doesnt change value (In React-Router)

Here is my problem is that I need to change the value of the useState in parent component, from the child component(s). but the child components are Routes, so I passed them through like this;
const NurseInterface = () => {
const [wristbandID, setWristbandID] = useState(1);
...
<Switch>
<Route
exact path={`${path}/default-view`}
render={(props)=> (
<NurseDefaultView {...props} setWristbandID={setWristbandID}/>// Alternative way of Routing Components, allowing you to pass props to it
)}
/>
...
</Switch>
...
}
inside the Route is the NurseDefaultView which passes the state to Wristbands:
const NurseDefaultView = ({ setWristbandID }) => {
...
{wristbands ? <Wristbands wristbands={wristbands} setWristbandID={setWristbandID}/> : null}
}
and then Wristbands passes the state down to wristband cards through map()
//React-Spring
const transitions = useTransition(wristbands, wristband => wristband.id, {
from: { opacity: 0, transform:"translate(0px, -50px)" },
enter: { opacity: 1, transform:"translate(0px, 0px)" },
leave: { opacity: 0, transform:"translate(0px, -50px)" },
})
if (wristbands) {
return (
<>
{transitions.map(({item, key, props})=> (
item ? (
<WristbandCard key={parseInt(item.id)}
props={props}
nurseView={true}
setWristbandID={setWristbandID}
wristband={item}/> ) : null
))}
</>
)} else return <div>No Wristbands</div>
and WristbandCard looks this this:
const WristbandCard = ({ wristband, setWristbandID, setShowDetail, nurseView, props }) => {
...
const handleClick = () => {
if (nurseView) {
console.log(wristband.id);
setWristbandID(wristband.id);
} else {
console.log("SetShowDetail")
setShowDetail(true);
setWristbandID(wristband.id);
}
}
...
if (nurseView) return (
<Link className="right" to={`patient-cart`}>
<animated.article style={props} onClick={handleClick} className="card">
<header>
{battery(data)}
<img alt="Wifi Icon by Google Inc (placeholder)" src={Connection}/>
</header>
<h1>{wristband.name}</h1>
<div><img alt="transfer" src={Arrow}/><h3>{description(wristband, nurseView)}</h3></div>
<footer><img alt="transfer" src={Transfer}/></footer>
</animated.article>
</Link>
); else return (
<animated.figure style={props} onClick={handleClick} className={wristband.connected ? "card": "card red"}>
<header>
{battery(data)}
<img alt="Wifi Icon by Google Inc (placeholder)" src={Wifi}/>
</header>
<h3>{description(wristband)}</h3>
<h2>{wristband.name}</h2>
</animated.figure>
)
}
but it doesnt work. And ive console logged the value of it set at the top level inside the Route, and the value. But in the parent, it doesn't work.
Does anyone have any examples of this working in react-router? I havent seen any sadly.
This makes me think that maybe Routes cant handle Hooks as props and I need to implement Redux for this, but i'm hoping that there is maybe somewhere I went wrong? because this is the only case where I need something like this.
I would be grateful for any help. Thanks.
You can use context with a context hook to update a parent's state (wristband id) in a nested child component (setWristbandId).

ReactJS: How to map items in conditional statements

I have a react component that allows a user to click a button in the header and add different input types.
export default class ElementContainer extends Component {
render() {
return (
<div className="append-header">
<Headline buttonCheck={this.props.buttonCheck} />
<SubHeadline buttonCheck={this.props.buttonCheck} />
<URLButton={this.props.buttonCheck} />
</div>
)
}
}
I'm trying to implement react-beautiful-dnd into the project so that those inputs can be repositioned like a list in Trello.
Looking at that library (and other similar libraries), they use the data as lists in order to perform the drag and drop function.
The items in my app are added to the view by conditional rendering:
export default class InputShow extends Component {
render() {
const { node } = this.props;
return (
<div className="editor-div" >
{
(node.type === 'buttonA') ?
<textarea
//omitted code
id={node.id}
onChange={this.props.inputContentHandler} />
:
(node.type === 'buttonB')
?
<URLButton
url={this.url}
id={node.id}
title={this.title}
/>
:
""
}
</div >
)
}
}
I've tried to map the items by creating a state for items (additions)
export default class InputShow extends Component {
constructor(props) {
super(props)
this.state = {
additions: []
}
}
render() {
const { node } = this.props;
return (
<div className="editor-div" >
{this.state.additions.map(addition => (
<div key={addition.id}>
{
(node.type === 'buttonA') ?
<textarea
//omitted code
id={node.id}
onChange={this.props.inputContentHandler} />
:
(node.type === 'buttonB')
?
<URLButton
url={this.url}
id={node.id}
title={this.title}
/>
:
""
}
</div>
))}
</div >
)
}
}
I didn't get any errors, however now when I click on the buttons, no data is being displayed in the view. I've done simple maps in the past with API and local data but I've never tried it with ternary statements. Thanks for any feedback on a solution to this problem.
What you can do is separate the view logic from the code and create a functional component. Pass the values from the main as below:
{this.state.additions.map(addition => (
return <CustomTemplate id={addition.id}
nodeId={node.id} changeEvent={this.props.inputContentHandler}
nodeType={node.Type} url={this.url} title={this.title}/>))}
Create CustomTemplate something like this
const CustomeTemplate =(props)=>(
use props to get the values in the templates
)

React - onClick event not working correctly

I'm attempting to pass an onClick function as a prop to a child component in React. However, nothing is being logged to the console when the button is clicked. For now I'm just trying to console log to make sure the event is actually firing.
Any Ideas?
class App extends React.Component {
togglePallets = (pallet) => {
console.log('test');
}
render() {
return (
<div className="mainWrapper">
<div className="mainContainer">
<div>
<img src="images/picture-of-me.jpg" alt="Me"></img>
</div>
</div>
<SideBar toggle={this.togglePallets} showPallets={[this.state.showAboutPallet, this.state.showLanguagesPallet,
this.state.showProjectsPallet, this.state.showContactPallet]}/>
{this.state.showAboutPallet && <AboutPallet />}
{this.state.showAboutPallet && <LanguagesPallet />}
{this.state.showAboutPallet && <ProjectsPallet />}
{this.state.showAboutPallet && <ContactPallet />}
</div>
);
}
}
function SideBar(props) {
return (
<div className="sideBarContainer">
<Button icon={faUser} showAboutPallet={props.showPallets[0]} onClick={props.toggle}/>
</div>
);
}
What you have written is correct. But we can try it in another way using an arrow function.
onClick={(e) => props.toggle(e,data)}
And, make relevant changes in toggle function, so it may support multiple arguments.
Change your togglePallets to any of the below
togglePallets() {
console.log("test");
};
If you want to access event then
togglePallets(event) {
console.log("test");
};
Or
togglePallets=event =>{
console.log("teeventst");
};

Resources