I have a drag and drop field (called react-dropzone) that setState of a file.
I want the file.name to be rendered in the browser.
When tested, it produces no errors nor results. I can get other states to render in the normal way. Any suggestions?
Code:
class Home extends Component {
state = {
fileName: {}
};
handleOnDrop = file => {
this.setState({ file });
};
render() {
return (
<div>
<Dropzone onDrop={this.handleOnDrop} </Dropzone>
<h3>{this.state.file.name}</h3>
</div>
);}}
export default Home;
Thanks!
EDIT: The answer is that I was trying to pass a full array to a value not set up to take an array. In this case, I needed to pass a single element (file name) from file array to the state.
onDrop returns an array of accepted files.
handleOnDrop: (acceptedFiles) => {
acceptedFiles.forEach(file => {
console.log(file.name);
})
}
Docs
This might not be the answer, and I don't know if you pasted the whole code, but a closing tag is missing on your Dropzone component.
A sandbox demo would help imo
Related
At the moment I am trying to do a website on cruise ships using React in my spare time.
I have a working version on my Reviews branch, here https://github.com/RobertWSON/Personal-ship-project/tree/reviews.
However I am wanting to change how the Cruise Lines Page is displayed.
I would like to have Cruise Line Headings across the page.
When a Cruise Line Heading is clicked it expands to show a List of Ships for that Cruise Line and if you click again, it collapses to show just the Cruise Line Heading.
At the moment I am a bit confused, as to how I can make this work and I have not got it working just yet.
I have been working on this, on a different branch called robs-shipslist-under-cruiselines: here https://github.com/RobertWSON/Personal-ship-project/tree/robs-shipslist-under-cruiselines .
I have components called CruiseListHeader.jsx and ListofShips.jsx.
Just wondering if anyone can give me any advice on whether it's possible to do a ternary operator for this handleClick, that I have in my CruiseListHeader component?
It seems to me that the code inside my handleClick function is the code that causes the errors.
I think my state for opening and closing the ShipsList, so that's OpenshipsList and CloseshipsList, needs to be handled better.
How can I better deal with this?
Does anyone have any ideas that may help me solve this problem and make it work.
The following code is from my CruiseListHeader component
import React from 'react'
import {getCruiseLines } from '../api/api';
class CruiseListHeader extends React.Component {
constructor(props) {
super(props)
//setting intial state for cruise heading and shipsList and initialize cruiseHeaders as an empty array
this.state = {
cruiseHeaders: [],
shipsList: {isOpen:false}
}
//binding methods for Cruise Line Headers and Handle Click Function
this.setUpCruiseLines = this.setUpCruiseLines.bind(this),
this.handleClick = this.handleClick.bind(this)
}
componentDidMount() {
console.log('cdm')
this.setUpCruiseLines()
}
setUpCruiseLines() {
console.log('getcruiselines')
getCruiseLines()
.then(res => {
this.setState({
cruiseHeaders: res
})
})
}
/* There will be Headings for all the Cruise Lines.
When a Cruise Line Heading is clicked, it goes to ListofShips Component and the Ships List opens up for that Heading.
When user clicks on a Cruise Line Heading, when a Ships List is open, the Ships List Collapses.*/
handleClick(event) {
// Maybe do a ternary operator here before open and close functions
this.state.shipsList === isOpen ? OpenShipsList : CloseshipsList
OpenshipsList(event) {
this.setState = {shipsList: {isOpen:true}}
return
<div>
<ListofShips/>
</div>
}
CloseshipsList(event) {
this.setState = {shipsList: {isOpen: false}}
render()
}
}
// This renders at the start when the page loads and also when you close a list
render() {
return (
<React.Fragment>
<h3><button onClick = {this.handleClick}>{ship.cruise_line}</button></h3>
</React.Fragment>
)
}
}
export default CruiseListHeader
At the moment, when I do a yarn dev I am getting the following error
ERROR in ./client/components/CruiseListHeader.jsx Module build failed:
SyntaxError: Unexpected token, expected ; (42:29)
I would like to get rid of this error and display the page like I have described above.
As a beginning, to set isOpen correctly on the state, modify the onClick function handler as this:
handleClick(event) {
// this handleClick function should only handle the `isOpen` value in the state.
// Any renders supposibly to be made on the `render` method instead.
this.setState(prevState => ({
shipsList: {
isOpen: !prevState.shipsList.isOpen, //will reverse the prevState of isOpen.
}
}));
}
Now, Going to your render, we can handle the way you renderthe component that depends on the this.state.shipsList.isOpen this way:
render() {
//destructive declaration for isOpen from inside the shipsList in the state.
const { shipsList: { isOpen } } = this.state;
return (
<React.Fragment>
<h3>
<button onClick={this.handleClick}>
{ship.cruise_line}
</button>
</h3>
{
// Usually modals are shown at the bottom of the render return.
// it's better to use tenary `val ? component : null` rather than: (val && component)
// React accepts a component, or a null as return value, the second will return false if val was false.
isOpen ? <OpenShipsList /> : null
}
</React.Fragment>
)
}
PS: Please follow the comments inside the code above of each line, they should be enough illustrating what happened, if something was ambiguos, just let me know.
Hard to tell with the indentations, but is this.state.shipsList === isOpen ? OpenShipsList : CloseshipsList supposed to really be this.state.shipsList.isOpen ? OpenShipsList() : CloseshipsList();? Note that isOpen is a property of state.shipsList, and then the parens to invoke the calls to open/close the list, and also the semi-colon to end the line.
I think you probably really want your handleClick to simply toggle the open state and then use that state value to selectively render the list.
const handleClick = event => this.setState(
prevState => ({ shipsList: {isOpen: !prevState.shipsList.isOpen} })
);
render() {
const { shipsList: { isOpen } } = this.state;
return (
<Fragment>
{isOpen && <ListofShips />}
<h3>
<button onClick={this.handleClick}>{ship.cruise_line}</button>
</h3>
</Fragment>
)
}
I have a page with a child component for a browse button, on the parent component, with a callback I set the state with the browsed file.
For some reason, because of a different callback passed to an higher parent component the state is not being set with the attached file.
If I remove the second callback this.props.handleChange('attachment', file); everything works fine. Any idea why? (Nothing is wrong with the second callback, no errors etc)
Attachment page:
export default class Attachment extends React.Component {
state = {
attachment: {},
};
handleAddAttachment = file => {
this.setState({ attachment: file });
this.props.handleChange('attachment', file); // this causes the previous line to not working.
};
render() {
const { attachment } = this.state;
return (
<Fragment>
<div>
<div>
Do you have
<br />
something to <LineBreak />
show me?
</div>
<div css={attach}>Upload attachments here</div>
<AttachmentButton handleAddAttachment={this.handleAddAttachment} />
<AttachedFile attachment={attachment} />
</div>
</Fragment>
);
}
}
makeHandleChange method on parent component:
makeHandleChange = (pageName, change) => {
this.setState({
ticket: { ...this.state.ticket, [pageName]: change },
});
};
I think because your parent component gets new state so it rerenders and also causes the children to rerender and cancel the state. To keep your children intact you can set a key props to your child component or you can use shouldComponentUpdate. like this in your parent
<Attachment key="attachment-key" />
I don't know why this doesn't work, but you may want to try a workaround: passing the function as the second argument to setState.
See an example here:
react set state callback correct way to pass an argument
Since we don't know the context of what this.props.handleChange() does, I'm giving a generic answer. Try changing the function this way:
handleAddAttachment = file => {
this.setState({
attachment: file
}, () => {
this.props.handleChange('attachment', file);
});
};
I insert all different components into an array so that last clicked component will show on the top,
but I don't know how to delete the item with a button click inside the component
It seems I can't pass the function with key index inside the component...
So I don't know which component I clicked ...
Please help me figure it out.
ComponentA.js
....
render(){
return <button onClick{this.props.Delete}>Delete</button>
}
Parent.js
const List =({ComponentsList}) =>(
<div>
ComponentsList.map((item,i) => <div key={i}>{item}</div>)}
</div>)
Besides,
I also find people don't use this way to list items,
what if these components are all different?
What will people usually deal with it?
Thanks !
.
.
.
Edited:
Figure out one question about how to delete component
see the answer below
But I find State still exist !!!
Can any one help me ?
Thanks to my friend I find my mistake.
Maybe it is too stupid to make this kind of obvious mistake so I can find an answer on the website.
Hope it will hope someone who is a beginner and need some information when deal this kind of problem.
Parent.js
constructor(props) {
super(props)
this.state = {
listState:[<ComponentB type="AAAA" delete={this.RemoveItem} />, ...]
}
}
RemoveItem(dom) {
dom.currentTarget.parentNode.remove()
console.log("detect delete")
}
render() {
const mapToComponent = data => {
return data.map((contact, i) => {
return <div>{contact}</div>
})
}
return (
<div className="Graph-Panels">
{mapToComponent(this.state.PanelState)}
</div>
)
}
}
Let's say you hold the child components in a list. You can have it in the State.
inputList: []
Let's say there's a button on the parent component to add child components. Its code will look similar to this.
<div>
<Button variant= "light" className="button" onClick={this.onAddAnotherBtnClick}>
Add Another
</Button>
{this.state.inputList.map(function(input, index) {
return input;
})}
</div>
(You might have to bind the onAddAnotherBtnClick like this). In the state,
this.onAddAnotherBtnClick = this.onAddAnotherBtnClick.bind(this);
onAddAnotherBtnClick looks like this.
onAddAnotherBtnClick = (event) =>{
const inputList = this.state.inputList;
this.setState({
inputList: inputList.concat(<ChildComponent Id={inputList.length}
callbackDeleteButton={this.delete}/>)
});
}
This is the delete method.
delete = (Id) => {
delete this.state.inputList[Id];
this.setState({ inputList : this.state.inputList });
}
This is the Delete button on the child component.
<Button variant= "light" className="button" onClick={this.onDeleteButtonClick}>
Delete
</Button>
Here's the onDeleteButtonClick method.
onDeleteButtonClick = () => {
this.props.callbackDeleteButton(this.state.Id);
}
(You'll have to bind the method in the State like this).
this.onDeleteButtonClick = this.onDeleteButtonClick.bind(this);
What happens here is that I send the ID to each child component when it's created on the props. When the child component's delete button is clicked it sends its ID to the parent through a callback method the parent has supplied.
Im new in ReactJS...
I have a project with the following class components structure:
index.js
--app
--chat
--header
--left
--right
In the chat.js component, I make a google search with the api to retrieve images based on specific keyword... My intuitive solution was:
this.client.search("cars")
.then(images => {
for(let el of images) {
ReactDOM.render(<img src="{{el.url}}" syle="{{width: '100%'}}" />, document.querySelector('#gimages'));
}
});
It is correct? Or I may to use Components with stored states with flux (redux)?
Perhaps a simpler more conventional use of react would achieve what your require?
You could follow a pattern similar to that shown below to achieve what you require in a more "react-like" way:
class Chat extends React.Component {
constructor(props) {
super(props)
this.state = { images : [] } // Set the inital state and state
// model of YourComponent
}
componentDidMount() {
// Assume "client" has been setup already, in your component
this.client.search("cars")
.then(images => {
// When a search query returns images, store those in the
// YourComponent state. This will trigger react to re-render
// the component
this.setState({ images : images })
});
}
render() {
const { images } = this.state
// Render images out based on current state (ie either empty list,
// no images, or populated list to show images)
return (<div>
{
images.map(image => {
return <img src={image.url} style="width:100%" />
})
}
</div>)
}
}
Note that this is not a complete code sample, and will require you to "fill in the gaps" with what ever else you have in your current Chat component (ie setting up this.client)
This is not the way you should go, you don't need to use ReactDOM.render for each item. Actually, you don't need to use ReactDOM.render at all. In your component you can use a life-cycle method to fetch your data, then set it to your local state. After getting data you can pass this to an individual component or directly render in your render method.
class Chat extends React.Component {
state = {
images: [],
}
componentDidMount() {
this.client.search( "cars" )
.then( images => this.setState( { images } ) );
}
renderImages = () =>
this.state.images.map( image => <Image key={image.id} image={image} /> );
render() {
return (
<div>{this.renderImages()}</div>
);
}
}
const Image = props => (
<div>
<img src={props.image.url} syle="{{width: '100%'}}" />
</div>
);
At this point, you don't need Redux or anything else. But, if you need to open your state a lot of components, you can consider it. Also, get being accustomed to using methods like map, filter instead of for loops.
So I'm trying to add a pretty simple file upload to my React + Redux App and I found that Dropzone to be the most convinient way to do it. Here's my setup.
I have a FileInput component
import React from 'react'
import Dropzone from 'react-dropzone'
class FileInput extends React.Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
}
onChange(files) {
// For Redux form
if (this.props.input) {
const {input: {onChange}} = this.props;
onChange(files[0])
}
else if(this.props.onChange){
this.props.onChange(files[0])
}
else{
console.warn('redux-form-dropzone => Forgot to pass onChange props ?');
}
}
render() {
return (
<Dropzone onDrop={ this.onChange } {...this.props} >
Drag&Drop the image <br/> or click to select one
</Dropzone>
)
}
}
export default FileInput
And I use it on tha page like this:
<FileInput
onChange={(file) => console.log('dropped', file)}
className='add-avatar-dropzone'
activeClassName='dropzone-active'
/>
(console.log used for debugging purposes ofcource)
But when I try to drop a file, I get 2 log outputs. The first being the file I dropped, the second - some kind of a Proxy, probably provided by react itself...
I wonder how to get rid of that proxy and why is it doing that in the first place?
Thing I tried
Couple obvious problem-points I tried to eliminate, but did not seem to make any change.
Renaming the onChange function to something else like handleDrop and rewriting it as handleDrop = (files) => {
Removing the constructor (seems weird to assign something to itself)
It ended up being a simple matter and it's really silly for me not to think of it.
The onChange prop in the code above got passed on to Dropzone and then to the input field, wich was the source of the confusion.
I modified the code to work like this:
render() {
const { onChange, ...rest } = this.props
return (
<Dropzone onDrop={ this.onChange } {...rest} >
Drag&Drop the image <br/> or click to select one
</Dropzone>
)
}
That works just fine