React toggle button based on API return - reactjs

I am currently working on a React application.
As you can see in the following code, there are two buttons. I would like to show the grey button if the user hasn't created a Request (default). If the user has created a Request the button should be green.
Problem:
The main issue is, that the map function (API) no returning any value for "no Requests" so I am not able to identify "no Requests". That means that .isEmpty,.length, .indexOf,... and also the "if-else" is not working because there is nothing to validate.
const greenButton = (
<Button color="green" onClick={e => DeleteRequest(e, rest.id)}>Request</Button>
);
const greyButton = (
<Button color="grey" onClick={e => CreateRequest(e, props.reservationID)}>Request</Button>
);
return (
<div>
{greyButton}
{requests.map((rest, i) => (
<div key={i}>
{rest.requester === username
? <div>{greenButton}{rest.requester} <i
aria-hidden="true"
className="delete icon"
onClick={e => DeleteRequest(e, rest.id)} />
</div> : <div />}
</div>
))}
</div>
);
Result in the UI:
API:
Any Ideas? (If you need more information, I am happy to provide more details)

conditional rendering
Use a ternary on requests being a defined/truthy object and has a truthy length property. In the true branch map the requests, grey button in false branch. This covers requests being either initially (or returned from the API) undefined or an empty array [].
return (
<div>
{requests && requests.length ? requests.map((rest, i) => (
<div key={i}>
{rest.requester === username
? <div>{greenButton}{rest.requester} <i
aria-hidden="true"
className="delete icon"
onClick={e => DeleteRequest(e, rest.id)} />
</div> : <div />}
</div>
)) : (
{greyButton}
)}
</div>
);

Related

How to call a button which is declared commonly to two different positions in a web page in reactjs

I have written a class component in reactjs inside render i am calling a button in two different positons with two diff param.
{this.applebutton('tutor')}
{this.applebutton('student')}
And the button i have declared is :
applebutton = (type) => {
<div className="col-12 col-sm-12 mt-2">
<AppleLogin
clientId={globalVariable.APPLE_ID}
redirectURI="https://test.example.com/sign-up"
usePopup={true}
callback={(e) => this.getAppleToken(e, `${type}`)} // Catch the response
scope="email name"
responseMode="query"
render={renderProps => (
<div
className='btn btn-google'
onClick={renderProps.onClick}
disabled={renderProps.disabled}
>
<AppleFilled className='btn-apple' />
Continue with Apple
</div>
)}
cookiePolicy={'single_host_origin'}
/>
</div>
}
but i am getting this error
Expected an assignment or function call and instead saw an expression no-unused-expressions
What i am doing wrong i am new to reactjs
You need to define the component names with capital letters. For not render issue, you need to use a return before the write JSX. Also its better to usage of curly brackets for define parameters.
If you re-write like this, I believe that it will work.
AppleButton = ({type}) => {
return <div className="col-12 col-sm-12 mt-2">
<AppleLogin
clientId={globalVariable.APPLE_ID}
redirectURI="https://test.example.com/sign-up"
usePopup={true}
callback={(e) => this.getAppleToken(e, `${type}`)} // Catch the response
scope="email name"
responseMode="query"
render={renderProps => (
<div
className='btn btn-google'
onClick={renderProps.onClick}
disabled={renderProps.disabled}
>
<AppleFilled className='btn-apple' />
Continue with Apple
</div>
)}
cookiePolicy={'single_host_origin'}
/>
</div>
}
And you can use it on this way:
<AppleButton type="tutor" />

How to disable second form if first one is clicked

I have two fields that show a modal when clicked on. I want to make one disabled if I select anyone first. I am using react-bootstrap for this project
<>
<div className='add-edit-product'>
<div className='d-flex align-items-center justify-content-center error-info mb-3'>
<img src={`../../../assets/img/about/${data.currencyHedge && data.marginFinancing ? "error-info-success.png" : "error-info.png"}`} className='me-3' />
{data.currencyHedge && data.marginFinancing ?
<p className='success'>Risks are acceptable due to mitigants</p> :
<p className='error'>The below risks require your attention</p>
}
</div>
<div className='form'>
<h2 className='mb-3'>Exchange rate risk</h2>
{data.currencyHedge && data.marginFinancing ? <p>No risk</p> :
<div>
{/*clicking on either of this tab will show a modal*/}
<div className='risk-tab' onClick={() => { setcurrencyHedgeModal(true); setSelected("currencyHedge") }}>
<h3>Enter a currency hedge</h3>
<img src={`../../../assets/img/about/${data.currencyHedge ? "correct-success.png" : "correct (1).png"}`} />
</div>
<div className='risk-tab' onClick={() => {setfinancingSufficientlyModal(true); setSelected("marginFinancing")}}>
<h3>Margin the financing sufficiently</h3>
<img src={`../../../assets/img/about/${data.marginFinancing ? "correct-success.png" : "correct (1).png"}`} />
</div>
</div>
}
</div>
</div>
<div className='footer_'>
<button onClick={() => hendelCancel()} className="footer_cancel_btn">cancel</button>
<button onClick={() => { nextStep() }} className='footer_next_btn'> Next</button>
</div>
{currencyHedgeModal && <CurrencyHedgeModal show={currencyHedgeModal} onHide={() => setcurrencyHedgeModal(false)} getModalData={(e) => modalGetData(e)} type={selected} />}
{financingSufficientlyModal && <FinancingSufficientlyModal show={financingSufficientlyModal} onHide={() => setfinancingSufficientlyModal(false)} getModalData={(e) => setData({ ...data, marginFinancing: e })} />}
</>
how can I add the logic to disable the next field if anyone is selected first. the image below is the form. (the green check mark shows when each form is filled and saved)
Question answer 1
Yes, if you want both inputs to trigger the opening of the modals you have to set onClick prop on both.
This depends on implementation and what do you specifically mean by disable an input. There are a couple of possible scenarios:
2.1 You may add a disabled class like so:
className={`${isOpened ? "disabled" : ""}`}
and then write some css.
2.2 You might rewrite onClick to just return when the modal is already opened instead of opening the second modal like so:
onClick={() => {
if (isOpened) return
setOpened(true)
}
P.S. You may need to add a second boolean flag to your state if you want this behaviour on both modals / inputs
Original answer
You have to use useState hook with a boolean flag isOpened
const [isOpened, setOpened] = useState(false)
Then update the state when clicking on yout input field
onClick={() => setOpened(true)}
Finally in your input fields you can use isOpened to disable them however you want, using styles or other logic.
P.S. Don't forget to call setOpened(false) when closig your modal

An element is not removed from the array, how to fix it?

I have data that I get from api and through the map() method I display these "cards", each card has an image when clicked on which this image should receive an additional class. I implemented this by adding the index of the card to an array and now I can assign new classes to them, but I can't delete them
P.S. I have strict mode enabled, if it is disabled it removes extra classes on all other cards except the one I clicked on
//On the first click, it adds to the array, on the second click, it must delete it (index is written to the array)
function toggleFavoriteChanel(index) {
setFavorite(prevState => {
let returnArray = prevState;
if(prevState.includes(index)){
console.log(prevState)
console.log(index)
return returnArray.splice(prevState.indexOf(index), 1)
}else{
// here are 3 dots
return [..returnArray, index]
}
})
}
// <img src={star} alt="star".../>
{Array.isArray(props.visibleData) ? props.visibleData.map((chanel, index) => {
return (
<>
<div className="chanel__item" key={index}>
<img src={star} alt="star" onClick={() => props.toggleFavoriteChanel(index)} id={index} className={`star ${props.favorite.includes(index) ? 'active' : ''}`} />
<NavLink
onClick={() => props.updateData(index)}
end
style={{ textDecoration: 'none' }}
to='/ChanelPage'>
<img src={chanel.image} alt="" className="chanel__img" />
<div className="chanel__title"><div className="chanel__item-number">{index + 1}. </div>{chanel.name_ru}</div>
</NavLink>
</div>
</>
)
}) : null}
The issue is that you are setting favorite to the return value of splice, which is an array containing the deleted elements (from MDN docs on splice). What you want instead is to return returnArray after calling splice on it.
Just change this line in toggleFavoriteChanel:
return returnArray.splice(prevState.indexOf(index), 1)
to:
returnArray.splice(prevState.indexOf(index), 1);
return returnArray;
While the above should fix your issue, I would recommend approaching this problem in a different way if you are just trying to toggle a CSS class in response to clicking (assuming you don't need a list of the favorited cards at a higher level).
The approach is to define a component for the card and hold the isFavorite (clicked) state locally rather than in an array in an ancestral component.
Here's a rough example:
function Card(props) {
const [isFavorite, setIsFavorite] = React.useState(false);
return (
<div className="chanel__item">
<img
src={star}
alt="star"
onClick={() => setIsFavorite(prev => !prev)}
id={props.index}
className={`star ${isFavorite ? 'active' : ''}`}
/>
<NavLink
onClick={() => props.updateData(props.index)}
end
style={{ textDecoration: 'none' }}
to='/ChanelPage'>
<img src={chanel.image} alt="" className="chanel__img" />
<div className="chanel__title"><div className="chanel__item-number">{props.index + 1}. </div>{props.chanel.name_ru}</div>
</NavLink>
</div>
)
}

set first box clicked as a default at the first load

Im new in reactjs and I create a list of card by mapping in reactjs but I want my first card be clicked as a default at the first load what can i do for this code.
<div className="d-flex">
{data && data.length > 0
? data.map((item, index) => {
return (
<>
<div className="box-stock" onClick={() => selectData(item)}>
<div className="top-stock skewed p-5">
<h1>{item.symbol}</h1>
<strong className="text-center">
<span> (0.25)</span>
{item.stockNum}
</strong>
<Label className="text-center">
EPS:<span className="text-white">{item.EPS}</span>
</Label>
<Label className="text-center">
P/E:<span className="text-white">{item.PE}</span>
</Label>
</div>
</div>
</>
);
})
: 'no data'}
</div>
You can use useEffect hook to run program once it renders. Here, in useEffect pass empty array as a dependency so, that it runs only once.
useEffect(() => {
selectData(data[0])
}, [])
You can add click event on first load using javascript. The button will trigger the event on first load and you will get your first card clicked. Here is the code:
document.getElementsByClassName("box-stock").click()

How to use simulate(click) function in react unit test

I am beginner level in React test.
I have a component like a small carousel. I want to test if change state(currentPage) when i click. But i didn't. It throws that error message:
Method “simulate” is only meant to be run on a single node. 0 found
instead.
I didnt understand " what is node,which node ? "
That is my test code :
it("check if next button works properly", () => {
const wrapper = shallow(<QuickStartGuide />);
const nextButton = wrapper.find(".quick-start-guide-messages-control-button .next-button");
nextButton.simulate('click');
expect(wrapper.state().currentIndex).toEqual(1);
})
And component :
<div className={containerClassName}>
{isOpen &&
<div className={"quick-start-guide-content-wrapper"}>
<div className="quick-start-guide-header">
<MtButton text={"MINIMIZE"}
hasIcon={true}
onClick={this.handleTriggerButtonClick}
className={"quick-start-guide-minimize-button"}/>
<div className={"quick-start-guide-title"}>{"Quick Start Guide"}</div>
{!!guideMessages.length &&
<Fragment>
<div className={"quick-start-guide-messages"}>
<MtButton className={"quick-start-guide-messages-control-button prev-button"}
hasIcon={true}
onClick={this.handleGoToPrevMessageButtonClick}
isDisabled={!currentGuideMessageIndex}
text={"PREV"}/>
{guideMessages[currentGuideMessageIndex][0]}
<MtButton className={"quick-start-guide-messages-control-button next-button"}
hasIcon={true}
onClick={this.handleGoToNextMessageButtonClick}
isDisabled={currentGuideMessageIndex >= guideMessages.length - 1}
text={"NEXT"}/>
</div>
<div className={"quick-start-guide-indicators"}>
{guideMessages.map((item, index) => (
<IndicatorItem key={item[0].key}
order={index}
onClick={this.handleIndicatorItemClick}
className={classNames({
active: index === currentGuideMessageIndex
})}/>
))}
</div>
</Fragment>}
</div>
<div className={"quick-start-guide-content"}>
<div className={"quick-start-guide-action-buttons"}>
{guideActions.map(([text, handleActionButtonClick, isDisabled = false]) => (
<MtButton key={text}
text={text}
isDisabled={isDisabled}
visualType={BUTTON_VISUAL_TYPES.ROUNDED}
className={"quick-start-guide-action-button"}
onClick={handleActionButtonClick}/>
))}
</div>
</div>
</div>}
<MtButton className={"uick-start-guide-trigger-button"}
text={"GUIDE"}
onClick={this.handleTriggerButtonClick}
hasIcon={true}/>
</div>
The error you're receiving isn't related to the simulate(click) and is an issue with your selector because it's not finding anything to actually click.
Double check that this is the correct selector to use: wrapper.find(".quick-start-guide-messages-control-button .next-button");.
You can use console.log(wrapper.debug()) to check to see what elements are available to select.

Resources