react-dropzone child icon not changing on state change - reactjs

I have a react project and I am using the react-dropzone component:
import Dropzone from 'react-dropzone';
I want to make it stateful and show different images and text based on the state. I defined my states as:
const status = {
ready: 'ready',
preview: 'preview',
error: 'error',
requested: 'requested',
success: 'success',
failed: 'failed',
};
The state can change based on user actions (so when they drag a file onto the dropzone I update status in state as follows:
onDrop(acceptedFiles, rejectedFiles) {
// do some stuff here...
this.setState({ status: status.preview });
}
My render method is a three step process:
1. the actual render methos
render() {
const config = {
iconFiletypes: ['.xlsx'],
showFiletypeIcon: true,
};
return (
<div style={{ marginBottom: '30px' }}>
<Dropzone
config={config}
onDrop={files => this.onDrop(files)}
//className="dropzone"
multiple={false}
>
{this.renderDropZoneContent()}
</Dropzone>
</div>
);
}
choose what to render based on state:
renderDropZoneContent() {
switch (this.state.status) {
case status.ready:
return this.renderReadyState();
case status.preview:
return this.renderPreviewState();
// and on down for each state / status + default case...
}
}
and finally the code to render each case as functions:
renderPreviewState() {
return (
<div style={{ marginTop: '35px', textAlign: 'center' }}>
<i className="far fa-file-excel" style={{ verticalAlign: 'middle', fontSize: '50px' }} />
{/* There is more jsx here but I removed it for clarity */}
</div>
);
}
renderReadyState() {
return (
<div style={{ marginTop:'35px', textAlign:'center'}>
<i className="fas fa-cloud-upload-alt" style={{ verticalAlign: 'middle', fontSize: '50px' }} />
</div>
);
}
Nothing too crazy. My problem is that as the state changes, the text updates but the icon does not. This is an interesting problem because the logic of the application works, but its the specific element that does not get updated. Even more interesting is that I tried wrapping the entire return in another div and got the error: Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. I'm banging my head against the wall. If anyone has come across this before and have any tips it is greatly appreciate!!

Probably a conflict with how Font Awesome and React handle rendering.
If you are using React we recommend the react-fontawesome package or Web Fonts with CSS.
https://fontawesome.com/how-to-use/on-the-web/using-with/react

Related

How can I create a React functionnal component and get HTML attributes and ref in props with typescript?

I'm trying to make a Trello like, I'm using react-beautiful-dnd and started from the classical example.
This is where I render the card (I just replaced the div with Task, my component)
<Task
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={{
userSelect: "none",
padding: 16,
margin: "0 0 8px 0",
minHeight: "50px",
...provided.draggableProps.style,
}}
>
{item.content}
</Task>
In my component, I want to get all div attributes passed in the code above.
So I did this :
import React from "react";
const Task: React.FC<JSX.IntrinsicElements["div"]> = (props) => {
const { children } = props;
return (
<div className="card card-green" {...props}>
<div className="card-content">
<div className="content">{children}</div>
</div>
</div>
);
};
export default Task;
It looks good, excepted for the "ref", I got a message in my console when I try to drag my card
Invariant failed: Cannot get dimension when no ref is set
And indeed my ref is always undefined.
Do you have an idea ?
I tried several examples found but no one works. I think I am missing a type somewhere

React Typescript page - window.print is printing unexpected

I have a react typescript page. It's basically composed of a title and some elements on top, an inner page, and then a side panel with buttons. I was tasked with adding a "print page" to the side panel which should print some third component and open up a print dialogue for it (the window.print for the inner page).
I have 2 problems so far:
when rendering this on the print button, the entire print doesn't work at all (nothing happens). I was able to solve this by setting the onprint to happen on page load. Then it opens the print dialogue on page load but still doesn't work on the print button.
the window.print is printing the entire context of all elements on the page, instead of just new component i'm trying for.
here is part of the code:
const printCoversheet = () => {
setCoversheetData({
documentId: document.id,
somedata: somedata?.data ?? '',,
});
console.log(coversheetData);
};
useEffect(() => {
!isEmpty(coversheetData) && window.print();
window.onafterprint = () => setCoversheetData({} as VerificationErrorCoversheetProps);
}, [coversheetData, setCoversheetData]);
<div css={{ display: 'flex', justifyContent: 'flex-start', marginTop: 16 }}>
<Button
variant="outlined"
size="large"
color="secondary"
onClick={() => printCoversheet()}
>
Print Coversheet
</Button>
</div>
{!isEmpty(coversheetData) && (
<div css={{ display: 'none', '#media print': { display: 'block' } }}>
{!coversheetData.verificationFailures.length ? (
<CoversheetPrint {...coversheetData} />
) : (
<VerificationErrorCoversheet {...coversheetData} />
)}
</div>
)}
I had to do the opposite of this in all other divs
In other divs I did:
window.print() all together didn't seem to work on localhost

Redux-Persist Changes State after Application is ReHydrated

Working on something similar to Jotform.
This is my state:
const InitialState = {
BaseContainerEl: []
}
This is my approach, let's say I want to create Full Name fields (i.e three inputs with first, middle and last names). So, I create a component that renders a p-tag and onClick it runs the updateBase function where I stored the exact result (i.e the 3 inputs) in a variable 'el' and it update's the state using the virtual DOM created and stored in 'el'.
And it works fine, because after updating the state I render the element.
const FullName = (props: any) => {
const updateBase = () => {
const key = uuidv4();
const el = (
<BaseContainer key={key} id={key}>
<InputCustom
placeholder='First Name'
type='text'
label='First Name'
formControl='form-control form-control-sm'
style={{ width: '90%' }}
/>
<InputCustom
placeholder='Middle Name'
type='text'
label='Middle Name'
formControl='form-control form-control-sm'
style={{ width: '90%' }}
/>
<InputCustom
placeholder='Last Name'
type='text'
label='Last Name'
formControl='form-control form-control-sm'
style={{ width: '90%' }}
/>
</BaseContainer>
);
props.updateBaseContainerEl(el)
}
return (
<p draggable onClick={updateBase} className="text-muted" style={{
borderRadius: '2px',
boxShadow: ' 20px 20px 60px #bebebe-20px -20px 60px #ffffff',
}}>
<i className="fe fe-user" style={{
}}></i>
Full Name
</p>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
This the problem:
I am persisting the state using redux-persit, now when I refresh the application it rehydrates and after rehydration the virtual DOM stored in the state (i.e the react component) changes with some properties and values missing. According to the redux-persist docs, there are some values in javascript that cannot be represented in json.
So, it errors out: Uncaught Error: Objects are not valid as a React child (found: object with keys {type, key, ref, props, _owner, _store}). If you meant to render a collection of children, use an array instead.
Below are images of what it looks like before rehydration and after.
Is there any way around this?

http://localhost:3000/[object%20Object] not found 404

In my react app, this is an array of filenames I get from server side
const photos = ["01-1913.JPG", "01-1913.1.jpg", "01-1913.2.jpg"]
and here is how I use it with JSX
{
photos.map(entry => {
return (
<div key={entry}>
<PhotoItem key={entry} url={`${process.env.REACT_APP_NODE_SERVER}/${entry}`} />
</div>
)
})
}
const PhotoItem = (url) => {
return (
<img
src={url}
onError={this.addDefaultSrc}
alt="photoItem"
style={{
width: "500px",
height: "600px",
border: "1px solid #123C69",
}}
></img>
);
};
```
I can not figure out why I am not getting the photo (only the dummy photo from the onError event I've used) and if it has anything to do with the Object%object error. Any help would be appreciated.
As mentioned in the comments, the PhotoItem component should look like this:
// Note that here props are named "props" instead of "url"
const PhotoItem = (props) => {
return (
<img
src={props.url}
onError={this.addDefaultSrc}
alt="photoItem"
style={{
width: "500px",
height: "600px",
border: "1px solid #123C69",
}}
></img>
);
};
Note that the first argument that a react component receives is props. So even if you name it url, the value that you are looking for url lives in url.url.
I also recommend to deconstruct your props like this:
const PhotoItem = ({url}) => {
return (
<img
src={url}
...
></img>
);
};
I faced this error on the developer console on a Next.js project right after upgrading Next from v10 to v12.
Turns out using an image as <img src={require()}/> is not working anymore, and throws this error.
Instead to fix the issue, you need to use Next's (almost) drop in replacement of Image component as;
import Image from 'next/image'
...
<Image src={require()}/>
This will fix the issue, if your Next project is throwing this error.

React keep state of previous component and render new state

I am working on a news app where I call an API and display cards of trending news.(picture 1)
<div style={{display: 'flex', flexWrap: 'wrap', padding: 20, alignItems: 'center', justifyContent: 'center' }}>
{this.state.news.map(news => (
<NewsCard
key={news.url}
_id={news.url}
description={news.description}
image={news.urlToImage}
source={news.source.name}
title={news.title}
summary={this.state.summary}
onExpand={this.handleDetailClick.bind(this, news.url)}
onSave={this.handleDetailClick.bind(this, news.url)}
/>
))}
</div>
Upon expanding a card it the shows a gist of the article after calling a second API.(picture 2)
handleDetailClick = link => {
// console.log('hadle detail', link);
API.summarize(link)
.then((res) => {
// console.log(res.body);
this.setState({
summary: res.body
})
// console.log(this.state.summary);
})
}
However I am stuck on this issue whereupon calling the gist API on a second card the first card gets the gist of the second card. How do I prevent this from happening?(picture 3)
So far I've tried using the componentDidUpdate lifecycle method to compare states and keep the state persistent for the first card but I am not able to figure this out.
Any pointers?
You'll need to add in some way of associating the summary data with the correct news item. Currently your program just gives the current summary to all NewsCard components.
The simplest way to do this with your current code would be to also record the url that the summary came from in your main component's (the parent of the NewsCard components) state as well. Then only pass the summary to the correct news item.
For example (adding summaryUrl to state) (note the summary= line):
<div style={{display: 'flex', flexWrap: 'wrap', padding: 20, alignItems: 'center', justifyContent: 'center' }}>
{this.state.news.map(news => (
<NewsCard
key={news.url}
_id={news.url}
description={news.description}
image={news.urlToImage}
source={news.source.name}
title={news.title}
summary={news.url === this.state.summaryUrl ? this.state.summary : ""}
onExpand={this.handleDetailClick.bind(this, news.url)}
onSave={this.handleDetailClick.bind(this, news.url)}
/>
))}
</div>
So we only pass this.state.summary to the NewsCard if the url for the current news item matches the url for the summary.
Then the handleDetailClick method should be updated to:
handleDetailClick = link => {
// console.log('hadle detail', link);
API.summarize(link)
.then((res) => {
// console.log(res.body);
this.setState({
summary: res.body,
summaryUrl: link,
})
// console.log(this.state.summary);
})
}
Another way you might consider doing this is by passing the second API call off to the NewsCard component. So each NewsCard would have its summary as part of its own state and would fetch the summary when clicked on. This way there would be no confusion about which news item the summary belongs to. It could also help clean up your parent component by delegating tasks/data to children as the component grows.

Resources