How to make react child component using conditional - reactjs

So usually we create child component of react using code seems like this :
const component =(<button>Bla Bla</button>)
How you can create that using conditional? I have to try this one :
const component =(()=>{
if(true){
return(<button>Bla Bla</button>)
}else{
return null
}
})
but that code throw error : Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
How to write that code properly ?

You can do that by simply passing a prop to it. Make the component like:
const Button = ({ display }) => {
return <>
{display && <button>I am Button<button>}
</>
}
Now if you want to display it you can just call it as:
<Button display={true} />
Hope this works for you.

You can do it like this
const component = true ? (<button>Bla Bla</button>): null
true can be any conditional you want to check

const myComp = ({value}) =>(
<div>
{
value &&
<myComp />
}
<div>
From my comment above. One way is to use conditional rendering in you JSX code

const bio = this.state.displayBio ? (
<div>
This is true false condition
<button onClick={this.toggleDisplay}>Show Less</button>
</div>
) : (
<div>
<button onClick={this.toggleDisplay}>Show More</button>
</div>
);

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

How to link a text input with associated error message in a reusable React component

I have a React component called TextInput which looks like this:
const TextInput = React.forwardRef<HTMLInputElement, ITextInputProps>(
({ error, ...props }, ref) => {
return (
<>
<input
type="text"
aria-describedby="" // 👈 see here
ref={ref}
{...props}
/>
{error && (
<p> // 👈 how can I reference this?
{error}
</p>
)}
</>
)}
)
For accessibility, I am trying to link the error message to the field using aria-describedby.
In a normal HTML document I would just use the id of the paragraph tag, but with reusability in mind, I would like to learn how to do this in a React-like way.
The use of refs comes to mind, but I'm not sure how to apply it in this scenario.
I would say that the best way to do this is to create a required prop that handles the ID and you apply that ID on the aria fields.
With that in mind, if what you wanted is to generate the data dinamically you could use a useEffect running it only once and setting a state inside it.
function Component() {
const [describedBy, setDescribedBy] = useState()
useEffect(() => {
setDescribedBy(generateRandomString());
}, [])
return (
<div aria-describedby={describedBy}></div>
)
}

React: Setting and updating based on props

Currently I am facing the problem that I want to change a state of a child component in React as soon as a prop is initialized or changed with a certain value. If I solve this with a simple if-query, then of course I get an infinite loop, since the components are then rendered over and over again.
Component (parent):
function App() {
const [activeSlide, setActiveSlide] = useState(0);
function changeSlide(index) {
setActiveSlide(index);
}
return (
<div className="app">
<div className="app__nav">
<Button icon="FiSun" handler={changeSlide} active={activeSlide} index="0" />
<Button icon="FiSettings" handler={changeSlide} active={activeSlide} index="1" />
</div>
</div>
);
}
Component (child):
function Button(props) {
const Icon = Icons[props.icon];
const [activeClass, setActiveClass] = useState("");
// This attempts an endless loop
if(props.active == props.index) {
setActiveClass("active");
}
function toggleView(e) {
e.preventDefault();
props.handler(props.index);
}
return(
<button className={activeClass} data-index={props.index} onClick={toggleView}>
<Icon />
</button>
)
}
Is there a sensible and simple approach here? My idea would be to write the if-query into the return() and thus generate two different outputs, even though I would actually like to avoid this
The React docs have a nice checklist here used to determine if something does or does not belong in state. Here is the list:
Is it passed in from a parent via props? If so, it probably isn’t state.
Does it remain unchanged over time? If so, it probably isn’t state.
Can you compute it based on any other state or props in your component? If so, it isn’t state.
The active class does not meet that criteria and should instead be computed when needed instead of put in state.
return(
<button className={props.active == props.index ? 'active' : ''} data-index={props.index} onClick={toggleView}>
<Icon />
</button>
)
This is a great use of useEffect.
instead of the if statement you can replace that with;
const {active, index} = props
useEffect(_ => {
if(active == index) {
setActiveClass("active");
}
}, [active])
The last item in the function is a dependency, so useEffect will only run if the active prop has changed.
React automatically re-renders a component when there is a change in the state or props. If you're just using activeClass to manage the className, you can move the condition in the className as like this and get rid of the state.
<button className={props.active === props.index ? 'active' : ''} data-index={props.index} onClick={toggleView}>
<Icon />
</button>
however, if you still want to use state in the child component, you can use the useEffect hook to to update the state in the child component.
Try to use the hook useEffect to prevent the infinite loop. (https://fr.reactjs.org/docs/hooks-effect.html)
Or useCallback hook. (https://fr.reactjs.org/docs/hooks-reference.html#usecallback)
Try this and tell me if it's right for you :
function App() {
const [activeSlide, setActiveSlide] = useState(0);
const changeSlide = useCallback(() => {
setActiveSlide(index);
}, [index]);
return (
<div className="app">
<div className="app__nav">
<Button icon="FiSun" handler={changeSlide} active={activeSlide} index="0" />
<Button icon="FiSettings" handler={changeSlide} active={activeSlide} index="1" />
</div>
</div>
);
}

React conditional rendering bug even with single parent and child list [duplicate]

This question already has answers here:
How can I return multiple lines JSX in another return statement in React?
(8 answers)
Closed 4 years ago.
Learning a bit of React but it seems to me like there's a conditional rendering bug with React itself.
Suppose I have a Foo component like so:
foo.js
import React, { Component } from 'react';
class Foo extends Component {
render() {
const isLoggedIn = this.props.isLoggedIn;
return(
<div>
{ isLoggedIn ? (
<div>one</div><div>two</div>
) : (
<div>one</div><div>two</div><div>three</div>
)}
</div>
);
}
}
export default Foo;
and I use it like so:
app.js
import React, { Component } from 'react';
import Foo from './components/foo';
class App extends Component {
render() {
return (
<div>
<Foo isLoggedIn={false} />
</div>
);
}
}
export default App;
This produces the error:
Syntax error: Adjacent JSX elements must be wrapped in an enclosing tag
Please note the above Foo component, there is only a single parent div being returned not array. If it was an array, then yes I agree with the error.
The official example given in the React document's example is like this:
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
</div>
);
}
https://reactjs.org/docs/conditional-rendering.html
Does this look like a bug in React to anyone?
Update
Based on the answers and comments given here, the implied behaviour of React is ternary operators inside the render() function comes with it's own render calls behind the scenes, acting like a virtual component, which would mean an extra layer of <div> needs to be wrapped around the list of my child elements.
Emberjs Foo component
My confusion arise from the fact I have done some Emberjs development in the past and a component like this works as expected:
<h3>Foo component</h3>
{{#if isLoggedIn}}
<div>one</div><div>two</div>
{{else}}
<div>one</div><div>two</div><div>three</div>
{{/if}}
Thanks for the explanation from everyone nonetheless.
You are returning the 2 or 3 divs in the condition. Instead you should wrap them into on div and return.
Notice the wrapper div below.
{ isLoggedIn ? (
<div className='wrapper'><div>one</div><div>two</div><div>
) : (
</div className='wrapper'><div>one</div><div>two</div><div>three</div></div>
)}
Also note that there is small typo below
<div>one</div><div>two</div><div>three</div
You have a syntax error
<div>one</div><div>two</div><div>three</div
should be
<div>one</div><div>two</div><div>three</div>
Did you try adding ?
return(
<div>
{ isLoggedIn ? (
<Fragment><div>one</div><div>two</div><Fragment>
) : (
<Fragment><div>one</div><div>two</div><div>three</div><Fragment>
)}
</div>
);
Syntax error: Adjacent JSX elements must be wrapped in an enclosing tag?
you are returning multiple sibling JSX elements in an incorrect manner.
In Foo:
return(
<div>
{ isLoggedIn ? (
<div>one</div> //are siblings without
<div>two</div> //wrapping in container element.
) : (
<div>one</div> //are siblings without
<div>two</div> //wrapping in
<div>three</div>//container element.
)}
</div>
);
Right approach :
return (
<div>
{isLoggedIn
? (
<div> //add wrapper
/...
</div>
)
: (
<div> //add wrapper
//...
</div>
)}
</div>
);
Or
If you are using React16 then you can use React.Fragement as well:
e.g.
<React.Fragment>
<div>one</div>
<div>two</div>
</React.Fragment>
You need to wrap the element rendered in the condition
return(
<div>
{ isLoggedIn ? (
<div><div>one</div><div>two</div></div>
) : (
<div><div>one</div><div>two</div><div>three</div></div>
)}
</div>
);
Note the extra div around the nested conditional elements

passing mobx-react-form object as a prop

I'm trying to pass mobx-react-form object as a prop to a function component I created. the problem is when I call my component like that
<NameEditor form={ newFolderForm }/>
I do get the form argument inside NameEditor component but it doesn't let me 'edit' the field .. its like the field isn't editable,
but when I call the component like that
{ NameEditor({ form: newFolderForm }) }
it works perfectly fine, what am I missing ? shouldn't the both ways be the same thing in function components ?
edit: here is how I fetch the form
const NameEditor = ({ form }) => (
<form onSubmit={ form.onSubmit }>
<input { ...form.$('name').bind() }/>
<p>{ form.$('name').error }</p>
<button>Save</button>
</form>
)
thanks
make sure you are using observer() on you're function component, from the code you showed there I think you missed this part.
const NameEditor = observer(({ form }) => (
<form onSubmit={ form.onSubmit }>
<input { ...form.$('name').bind() }/>
<p>{ form.$('name').error }</p>
<button>Save</button>
</form>
))
https://mobx.js.org/refguide/observer-component.html
read how it works with Stateless function components

Resources