I have render in React component. If a condition is fulfilled I want to do something, in this case to show a message when hover over an icon but otherwise I don't want it to do anything.
I tried with an if condition but it doesn't work. This is my code:
render() {
const text= this.checkSomething();
return (
{
if (text.length > 0) {
<ActionBar>
<div>
<Icon type="..."/>
//do something here
</div>
</ActionBar>
}
}
But I get the following error:
A valid React element (or null) must be returned. You may have
returned undefined, an array or some other invalid object.
I know that I return something that doesn' exist if text.length == 0, but is there any way to make it work, like don't return anything if the condition is not met?
You cannot have an if within the return of the render() function. Only ternary and short-circuit operations will work inside the return.
It's best to perform as much logic before your return.
Try this instead:
render() {
const text= this.checkSomething();
if(!text.length) return null;
return (
<ActionBar>
<div>
<Icon type="..."/>
//do something here
</div>
</ActionBar>
);
}
}
In render, you need to return either a React.Element or null, so the shortest way could be like:
render() {
const text = this.checkSomething();
return (
text.length > 0 ? (
<ActionBar>
<div>
<Icon type="..."/>
//do something here
</div>
</ActionBar>
) : null
);
}
Read more about this here: https://facebook.github.io/react/docs/react-dom.html#render
You are not returning the Component. Try this code:
render()
{
const text = this.checkSomething();
if (text.length > 0) {
return ( <ActionBar>
<div>
<Icon type="..."/>
//do something here
</div>
</ActionBar> )
} else {
return null;
}
}
You are on the right path. Just a little mistake. You should condition it like so:
render() {
const text= this.checkSomething();
if (text.length > 0) {
return (
<div>
// do something
</div>
);
}
// if condition is not met
return (
<div>
<h1>please wait while data is loading..</h1>
</div>
);
}
Its simple check for condition and return component or just null
render() {
const text= this.checkSomething();
if (text.length > 0) {
return <ActionBar>
<div>
<Icon type="..."/>
</div>
</ActionBar>
}else{
return null
}
}
Here is working example.
class App extends React.Component {
construct() {
//...
}
sometext() {
return '123213';
}
render() {
return(
<div>
{(this.sometext().length > 0) ? <ActionBar /> : null}
</div>
);
}
}
const ActionBar = () => (
<div>Super</div>
)
ReactDOM.render(
<App />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
You can wrap it in a function and then call itself, all within the return.
return (
<div>
{(() => {
if (catSay == "maow") return <p>meow</p>;
})()}
</div>
);
Related
i want to return jsx if some condition is true if not undefined should be returned.
below is my code,
const showInfo = (item) {
return (
<div>
<div>
<span>name</span>
</div>
<div>
<button>click</button>
</div>
</div>
);
}
const Parent = () => {
return (
<Child
onDone = {({item}) => {
notify ({
actions: (condition === 'value1' || condition === 'value2' ) &&
showInfo(item) //should put this condition into showInfo method
})
}}
/>
);
}
what i am trying to do?
the above code works. but now i want to put the condition inside the showInfo method. so if condition is true return jsx and if condition is false should return undefined.
what i have tried?
I have tried something like below
const showInfo = (item) {
return
{(condition === 'value1' || condition === 'value2' ) ? <div>
<div>
<span>name</span>
</div>
<div>
<button>click</button>
</div>
</div>
: undefined
}
);
}
const Parent = () => {
return (
<Child
onDone = {({item}) => {
notify ({
actions: showInfo(item) //error here
})
}}
/>
);
}
but the above tried code, gives error "Type 'void' is not assignable to type 'ReactNode'" at actions statement.
could someone help me with this. i am not sure if i have used ternary operator properly. thanks.
EDIT
after trying one of the answers provided,
notify is a method that is returned from usehook
and it evaluates to the component below
const Something: React.FC<SomethingProps> = ({
description,
actions,
...props
}) =>
(
<Header>
<Title>{title}</Title>
</Header>
{(description ||actions) && (
<Body> //this is displayed
{description && <Description>{description}</Description>}
{actions && <Actions>{actions}</Actions>}
</Body>
)}
);
here the component is displayed when the condition fails in showInfo component.
in showInfo i am returning undefined if condition fails but still in the Something component the is displayed even though i have {description || actions}
i am not sure what is happening here.what is the condition i have to check for actions to not display in this case
i have tried
{(description ||actions !== 'false') && (
<Body> //this is displayed
{description && <Description>{description}</Description>}
{actions && <Actions>{actions}</Actions>}
</Body>
)}
and this works. i am wondering why i should specifically mention
actions !== 'false'
instead of actions only
could someone help me with this. thanks.
If you want to return jsx from function you should wrap them inside some component. In this case you cen use <React.Fragment> or just <>. Another problem which I can see is that you probably forgot about arrow in you arrow function. Also don't know from where variable names condition comes from.
const showInfo = (item) => {
return (
<>
{ condition === "value1" || condition === "value2" ? (
<div>
<div>
<span>name</span>
</div>
<div>
<button>click</button>
</div>
</div>
) : undefined}
</>
);
};
Wouldn't it be better to use the useState or useEffect hooks?
I am trying to show the horizontal line <hr> tag, when the conditions render for second time. For example, When the data renders first time, I dont want the <hr> tag, but as it renders the second time, I expect hr tag to come between the two data's. I tried but couldn't find anything. Can anyone help me with this
{
this.props.details && this.props.details.length > 0
? this.props.details.map((data) => (
<>
<div>{data} </div>
</>
))
: null;
}
You can simply maintain a state say isSecondRender and set it to false. In componentDidMount, you can set it to true(i.e. 2nd render).
In jsx, you can conditionally render hr tag.
Working demo
code snippet
class MyComp extends React.Component {
state = {
isSecondRender: false
};
componentDidMount() {
this.setIsSecondRender();
}
setIsSecondRender = () => {
setTimeout(
() =>
this.setState(prev => ({
...prev,
isSecondRender: !prev.isSecondRender
})),
3000
);
};
render() {
return this.props.details && this.props.details.length > 0 ? (
<>
{!this.state.isSecondRender
? "first time rendering"
: "second time rendering done"}
{this.props.details.map(data => (
<>
<div> {data} </div>
{this.state.isSecondRender && <hr />}
</>
))}
</>
) : null;
}
}
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<MyComp details={["data-1", "data-2"]} />
</div>
);
}
Expected component behaviour:
- Load animation as defined by 'if' condition
- Then after 1000ms, the setTimeout function will change the state, thus rendering the 'else' part of the conditional
Additional Details
The component loads in fine, however, when I have another instance of the component created, both on alerts fire at the same time, and the this.render.state is set to true. This prevents the animation from loading as part of the conditional statement, since state.render is true... even though it should be false... Shouldn't each instance of this component display the default conditional behaviour?
Name.js Component
export class Name extends Component {
state = {
render: false,
};
componentDidMount() {
//alert('Test')
setTimeout(
function () {
//Start the timer
this.setState({ render: true }); //After 1 second, set render to true
}.bind(this),
1000
);
}
render() {
if (this.state.render === false) {
return (
<div className={styles.LContainer}>
<div className={styles.ldsRing}>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
);
} else {
console.log(this.state.render)
return (
<div className={styles.Container}>
<img
className={styles.imageO}
src={require("./Apple_logo_white.svg")}
/>
<div className={styles.textArea}>
<h1 className={styles.title}>{this.props.title}</h1>
<h2 className={styles.subtitle}>
{this.props.sub}
</h2>
<Input name="name" handleChange = {this.props.handleChange} />
<ScreenButton goDirection={this.props.backScreen} direction="back"/>
<ScreenButton goDirection={this.props.nextScreen} direction="next"/>
</div>
</div>
);
}
}
}
I'm also using a switch statement to render these components, could that have anything to do with it?
render() {
//Pulling information down from state:
const { step, name, interest, location } = this.state;
const values = { step, name, interest, location };
switch (step) {
case 1:
return (
<Welcome
nextScreen={this.nextScreen}
handleChange={this.handleChange}
values={values}
/>
);
case 2:
console.log(this.state.step);
//return <h1>GOLS</h1>
return (
<React.Fragment>
<Name
nextScreen={this.nextScreen}
backScreen={this.backScreen}
handleChange={this.handleChange}
values={values}
sub="Example text"
title="Example text"
/>
</React.Fragment>
);
case 3:
console.log(this.state.step);
return (
<React.Fragment>
<Name
nextScreen={this.nextScreen}
backScreen={this.backScreen}
handleChange={this.handleChange}
values={values}
sub="Example text"
title="Example text"
/>
</React.Fragment>
);
}
}
[1]: https://i.stack.imgur.com/U0v38.gif
I just checked and confirmed it's working like this.
import React, { Component } from 'react';
export default class Name extends Component {
constructor(props){
super();
this.state = {
render: false,
};
}
componentDidMount() {
//alert('Test')
setTimeout(
()=>{
this.setState({ render: true });
},1000
);
}
render() {
if (this.state.render === false) {
return (
<h1>Helloooo</h1>
);
} else {
console.log(this.state.render)
return (
<h1>Bye</h1>
);
}
}
}
See this: https://codesandbox.io/s/angry-breeze-gdmmy?file=/src/App.js
Good news! I figured out the solution when my <Input/> components started to behave similarly.
The problem was that each component (both <Name /> and <Input/>were behaving similarly to their first rendered instance, almost as if they were sharing state.
The fix was simple... assign a key value for each component instance.
Like this:
<Name key="Unique Key Value">
I have a product component that renders n number of sections. The code in the product component:
let sections = this.state.product.sections.map((section, idx) => {
return (
<Section sectionType={section.id} section={section} />
)
})
return (
<div>
{this.state.product.name}
{sections}
</div>
)
The code in the section component:
renderSection() {
switch (this.props.sectionType) {
case 'heroImage':
return (
<img src={image.url} />
)
case 'doublePane':
this.props.section.items.map((item, idx) => {
console.log(item.id);
if (1 === 1) {
return (
<div>hi</div>
)
}
})
default:
return (
<div>hey there</div>
)
}
}
render() {
return (
<div>
{this.renderSection()}
</div>
)
}
I added the 1===1 line just to make sure it would execute, but the output of my code is still
the heroImage case properly executes
the console log of item.id happens (so we definitely enter the doublePane block), but the code inside of the 1===1 block does not execute.
Any idea what's happening here to not allow me to run the code inside of the 1===1? Thanks in advance!
You need to return the result of the mapping function, in addition to returning within the mapping function:
renderSection() {
switch (this.props.sectionType) {
case 'heroImage':
return (
<img src={image.url} />
);
case 'setOfTwo':
return (
<div>
{this.props.section.items.map((item, idx) => {
console.log(item.id);
return (
<div>hi</div>
);
})}
</div>
);
default:
return (
<div>hey there</div>
)
}
}
Wrapping the return value in a div isn't necessarily required, but I prefer to return a consistent item (in this case a single JSX element) from functions.
All the docs for map use braces but the code below and most examples I see when dealing with React use parenthesis. I'm trying to figure out exactly what the difference is and what the code is doing.
When using braces, nothing renders unless I specifically add return. So my take is that the parenthesis act as some sort of inline function that automatically returns or React converts the result and inlines it into the JSX?
// Renders fine
render()
{
return (
<div className="item-list">
{
this.props.items.map(
( _item, _index ) => (
<ItemComponent
key={ _index }
name={ _item.name }
description={ _item.description }
/>
) )
}
</div>
);
}
// Nothing
render()
{
return (
<div className="item-list">
{
this.props.items.map(
( _item, _index ) =>
{
<ItemComponent
key={ _index }
name={ _item.name }
description={ _item.description }
/>
} )
}
</div>
);
}
// Renders fine
render()
{
return (
<div className="item-list">
{
this.props.items.map(
( _item, _index ) =>
{
return <ItemComponent
key={ _index }
name={ _item.name }
description={ _item.description }
/>
} )
}
</div>
);
}
Nothing to do with React, It is all about javascript
Curly braces saying it is a function body so we need to manually use return keyword
this.props.items.map(
( _item, _index ) =>
{ // Note: represent function body, normal javascript function
<ItemComponent
key={ _index }
name={ _item.name }
description={ _item.description }
/>
} )
According to arrow functions, has implicit return behavior hence so need to mention explicitly if single line expression.
render()
{
return (
<div className="item-list">
{
this.props.items.map(
( _item, _index ) => ( // Note: single line expression, so impilicit;y return our ItemComponent
<ItemComponent
key={ _index }
name={ _item.name }
description={ _item.description }
/>
) )
}
</div>
);
}
So parenthesis in an arrow function returns a single value, the curly braces are used when executing multiple lines of code and not just a simple return, so a manual return statement is required just because javascript can't know what among those lines to return.
materials.map(material => ({key:material.name}));
return an object
materials.map(material => {
let newMat = material.name+'_new';
return newMat;
});
we need to return since we are doing 1 or many lines of manipulation and trying to return end result