How to add paragraph while states changing in ReactJs - reactjs

I am quite new in React. I am trying to find a way for write something i.e. "actions loading .." while states implemented.
The problem is that the states added, then changes performed to the program. What I would like is while adding states, do something like <p>actions loading ..</p>
Here is my code:
{states.length > 0 && (
<div>
Actions loading ..
</div>
)}
The program here is, added the states, then added the paragraph.
Please see a screenshot of the states that added to my app.

You could use a ternary operator to show Actions Loading when the length of the state is zero.
{states.length === 0 ? 0 (
<div>
Actions loading ..
</div>
) : (
<p></p>
)}

It would really help if you add a bit more context for this component, but if you have something like this
const MyComponent = props => {
const [states, setStates] = React.useState([]);
React.useEffect(() => {
myRequest().then(setStates);
}, []);
return (
<div>
{states.length > 0 && (
<div>
Actions loading ..
</div>
)}
</div>
)
}
and you want to either show the states or a message, you could change the render to
<div>
{states.length > 0 ? (
<dix>
{states.map(state => <MyState/>)}
</div>
) : (
<div>
Actions loading ..
</div>
)}
</div>

Related

Filtered array Else condition renders on page refresh

I'm filtering data (coming from useFetch hook) array like so:
useEffect(() => {
const result = [...data].filter(
item => (!input || item.name.toLowerCase().includes(input.toLowerCase())) &&
(!select || item.dep === select)
);
setFilteredData(result);
}, [data, input, select]);
And outputing in the DOM like so:
{filteredData.length > 0 ? (
filteredData.map(({name}) => (
<Component name={name} />
))
) : (
<div>
<h2>NOT FOUND</h2>
</div>
)}
So I'm updating data array with results. The issue is, when I refresh the page, I can see "NOT FOUND" showing up briefly before the array results show up. How can I prevent this?
This problem is caused by the data appearing null at the beginning of the fetch operation. try this:
{!!filteredData.length > 0 ? (
filteredData.map(({name}) => (
<Component name={name} />
))
)}
or you can use the loading effect in the component.

how to fixed this ERROR TypeError: Cannot read properties of undefined (reading 'length') in react js

const Cart = ({ cartItems }) => {
return (
<div className="cart-items">
<div className="cart-items-header">Cart Items</div>
{cartItems.length === 0 && (
<div className="cart-items-empty">No items are added.</div>
)}
</div>
)
}
It seems that cartItems its undefined. For preventing this kind of error, its better to check whether cartItems is undefined or not. So Try this :
const Cart = ({ cartItems }) => {
return (
<div className="cart-items">
<div className="cart-items-header">Cart Items</div>
{cartItems && cartItems.length === 0 && (
<div className="cart-items-empty">No items are added.</div>
)}
</div>
)
}
As Ploppy said in one of the comments, it simply means that cartItems is undefined.
You now have 2 options
Ternary operator option:
In the same ternary operator you are currently using you can check if cartItems is undefined BEFORE accessing its length. Indeed it solves the problem but cannot be considered best practice.
const Cart = ({ cartItems }) => {
return (
<div className="cart-items>
<div className="cart-items-header">Cart Items</div>
{ cartItems && cartItems.length === 0 && (
<div className="cart-items-empty">No items are added.</div>
)}
</div>
)
}
TypeScript option:
Another option would be to convert the codebase you are currently using into a TypeScript project to handle in the most efficient way type errors you might encounter.
TypeScript React Documentation
Migrate a React App to TypeScript
I've made a simple CodeSandbox to show you how Type management is handled in your case.
TypeScript codesandbox

map is not a function on array React

I'm getting an error that map is not a function on my data.
I'm getting a response back from an api that is returning an array of objects. When I don't refresh the page I can view the results displayed just fine and even navigate to view them individually (when I click on see more). However, when I refresh the page I get the
error of "Map is not a function" on my props even though the results are displaying in the console log.
I'm lost here and can't figure out why it's doing that.
componentDidMount() {
this.props.getCanyons();
}
render() {
const { canyons } = this.props;
console.log(canyons)
return (
<section>
{canyons.map(canyon => (
<section key={canyon.canyon_id}>
<h3>{canyon.canyon_name}</h3>
<img src={canyon.canyon_pic} alt={canyon.canyon_name} />
<Link key={canyon.canyon_id} to={`/canyon/${canyon.canyon_id}`}>
<button>See More</button>
</Link>
</section>
))}
</section>
);
}
}
When the api failed or having lag time to get response, it may be undefined. This kind of checking prevent you to from such problem.
return (
{canyons && canyons.map(canyon => (
...skipped code
))}
)
Typescript provide feature of adding a ? before try to access the related Object type variable
//In typescript
{canyons?.map(canyon => (
...skipped code
))}
componentDidMount() {
this.props.getCanyons();
}
render() {
const { canyons } = this.props;
console.log(canyons)
return (
<section>
{ canyons !== '' || canyons.length > 0 ? //change here
canyons.map(canyon => (
<section key={canyon.canyon_id}>
<h3>{canyon.canyon_name}</h3>
<img src={canyon.canyon_pic} alt={canyon.canyon_name} />
<Link key={canyon.canyon_id} to={`/canyon/${canyon.canyon_id}`}>
<button>See More</button>
</Link>
</section>
))
:
null
}
</section>
);
}
}
Please follow the change. It should works for you...
Many browsers provide a live view when using console.log(). When the request not finished 'canyons' is undefined. Use
console.log(JSON.parse(JSON.stringify(obj)))
For this problem, try to set default value or check variable first

Unexpected Behavior After State Change in React Component

RenderImages = (): React.ReactElement => {
let selected = this.state.results.filter(x=>this.state.selectedGroups.includes(x.domain))
console.log(selected)
return(
<div className="results_wrapper">
{selected.map((r,i)=>{
let openState = (this.state.selectedImage==i)?true:false;
return(
<RenderPanel panelType={PanelType.large} openState={openState} title={r.domain+'.TheCommonVein.net'} preview={(openIt)=>(
<div className="result" onClick={openIt} style={{ boxShadow: theme.effects.elevation8}}>
<img src={r.url} />
</div>
)} content={(closeIt)=>(
<div className="panel_wrapper">
<div className="panel_content">{r.content}</div>
{this.RenderPostLink(r.domain,r.parent)}
<div onClick={()=>{
closeIt();
this.setState({selectedImage:2})
console.log('wtfff'+this.state.selectedImage)
}
}>Next</div>
<img src={r.url} />
</div>
)}/>
)
})}
</div>
)
}
When I change the state of 'selectedImage', I expect the variable 'openState' to render differently within my map() function. But it does not do anything.
Console.log shows that the state did successfully change.
And what is even stranger, is if I run "this.setState({selectedImage:2})" within componentsDidMount(), then everything renders exactly as expected.
Why is this not responding to my state change?
Update
I have tried setting openState in my component state variable, but this does not help either:
RenderImages = (): React.ReactElement => {
let selected = this.state.results.filter(x=>this.state.selectedGroups.includes(x.domain))
console.log(selected)
let html = selected.map((r,i)=>{
return(
<RenderPanel key={i} panelType={PanelType.large} openState={this.state.openState[i]} title={r.domain+'.TheCommonVein.net'} preview={(openIt)=>(
<div className="result" onClick={openIt} style={{ boxShadow: theme.effects.elevation8}}>
<img src={r.url} />
</div>
)} content={(closeIt)=>(
<div className="panel_wrapper">
<div className="panel_content">{r.content}</div>
{this.RenderPostLink(r.domain,r.parent)}
<div onClick={()=>{
closeIt();
let openState = this.state.openState.map(()=>false)
let index = i+1
openState[index] = true;
this.setState({openState:openState},()=>console.log(this.state.openState[i+1]))
}
}>Next</div>
<img src={r.url} />
</div>
)}/>
)
})
return(
<div className="results_wrapper">
{html}
</div>
)
}
https://codesandbox.io/s/ecstatic-bas-1v3p9?file=/src/Search.tsx
To test, just hit enter at the search box. Then click on 1 of 3 of the results. When you click 'Next', it should close the pane, and open the next one. That is what I'm trying to accomplish here.
#Spitz was on the right path with his answer, though didn't follow through to the full solution.
The issue you are having is that the panel's useBoolean doesn't update it's state based on the openState value passed down.
If you add the following code to panel.tsx, then everything will work as you described:
React.useEffect(()=>{
if(openState){
openPanel()
}else{
dismissPanel();
}
},[openState, openPanel,dismissPanel])
What this is doing is setting up an effect to synchronize the isOpen state in the RenderPanel with the openState that's passed as a prop to the RenderPanel. That way while the panel controls itself for the most part, if the parent changes the openState, it'll update.
Working sandbox
I believe it's because you set openState in your map function, after it has already run. I understand you think the function should rerender and then the loop will run once more, but I think you'll need to set openState in a function outside of render.
The problem is that even though you can access this.state from the component, which is a member of a class component, there's nothing that would make the component re-render. Making components inside other components is an anti-pattern and produces unexpected effects - as you've seen.
The solution here is to either move RenderImages into a separate component altogether and pass required data via props or context, or turn it into a normal function and call it as a function in the parent component's render().
The latter would mean instead of <RenderImages/>, you'd do this.RenderImages(). And also since it's not a component anymore but just a function that returns JSX, I'd probably rename it to renderImages.
I tire to look at it again and again, but couldn't wrap my head around why it wasn't working with any clean approach.
That being said, I was able to make it work with a "hack", that is to explicitly call openIt method for selectedImage after rendering is completed.
RenderImages = (): React.ReactElement => {
let selected = this.state.results.filter((x) =>
this.state.selectedGroups.includes(x.domain)
);
return (
<div className="results_wrapper">
{selected.map((r, i) => {
let openState = this.state.selectedImage === i ? true : false;
return (
<RenderPanel
key={i}
panelType={PanelType.medium}
openState={openState}
title={r.domain + ".TheCommonVein.net"}
preview={(openIt) => {
/* This is where I am making explicit call */
if (openState) {
setTimeout(() => openIt());
}
/* changes end */
return (
<div
className="result"
onClick={openIt}
style={{ boxShadow: theme.effects.elevation8 }}
>
<img src={r.url} />
</div>
);
}}
content={(closeIt) => (
<div className="panel_wrapper">
<div className="panel_content">{r.content}</div>
{this.RenderPostLink(r.domain, r.parent)}
<div
onClick={() => {
closeIt();
this.setState({
selectedImage: i + 1
});
}}
>
[Next>>]
</div>
<img src={r.url} />
</div>
)}
/>
);
})}
</div>
);
};
take a look at this codesandbox.

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