I have an app that uploads files via a standard <input type="file"/>. I'm trying to pass the file size of the chosen file(s) to the child to see if it's above a certain size, and if so, display an error. I know you have to pass state values down as props, but I'm unsure as to where/how to call the function to get an updated value. Any help is appreciated.
Edit: I am using the react jsonschema form to build the form: https://github.com/mozilla-services/react-jsonschema-form. Declaring the schemas before the Parent class.
Parent
const schema = {
type: 'object',
required: ['file'],
properties: {
file: { type: 'string', format: 'data-url', title: 'File' }
}
}
const FileWidget = (props) => {
return (
<input type="file" id="fileName" required={props.required} onChange={(event) => props.onChange(event.target.value)} />
)
}
const uiSchema = {
file: {
'ui:widget': FileWidget,
classNames: "uiSchema"
}
}
class Parent extends Component {
constructor(props) {
super(props);
this.state = { fileSize: 0 };
this.getFileSize = this.getFileSize.bind(this);
getFileSize(){
this.setState({fileSize: document.getElementById("fileName").files[0].size});
console.log("FILESIZE:: ", this.state.fileSize);
} //where to call to update the file size?
render() {
return (
<div className="container">
<FileUpload schema={schema} uiSchema={uiSchema} fileSize={this.state.fileSize} />
</div>
)
}
}
export default Parent;
Child
class Child extends Component {
constructor(props) {
super(props);
this.state = { formData: {} };
this.handleSubmit = this.handleSubmit.bind(this);
}
render() {
return (
<div className="container">
<Form
schema={this.props.schema}
uiSchema={this.props.uiSchema}
formData={this.state.formData}
onChange={({ formData }) => this.setState({ formData })}
onSubmit={this.handleSubmit}
>
<div>
<button type="submit" className="btn btn-info">Convert</button>
</div>
</Form>
<div hidden={this.props.fileSize > 100 ? false : true }><h4>File size exceeded.</h4></div>
</div>
)
}
}
export default Child;
class Parent extends Component {
constructor(props) {
super(props);
this.state = { fileSize: 0 };
this.getFileSize = this.getFileSize.bind(this);
getFileSize(){
this.setState({fileSize: document.getElementById("fileName").files[0].size});
console.log("FILESIZE:: ", this.state.fileSize);
} //where to call to update the file size?
componentDidMount(){
// you can call the getFilesize here
this.getFileSize();
}
render() {
return (
<div className="container">
<FileUpload fileSize={this.state.fileSize} />
</div>
)
}
}
export default Parent;
child Component
class FileUpload extends Component {
constructor(props) {
super(props);
this.state = { formData: {} };
this.handleSubmit = this.handleSubmit.bind(this);
}
render() {
return (
<div className="container">
<Form
formData={this.state.formData}
onChange={({ formData }) => this.setState({ formData })}
onSubmit={this.handleSubmit}
>
<div>
<button type="submit" className="btn btn-info">Convert</button>
</div>
</Form>
<div style={{display:this.props.fileSize > 100 ? "block": "none" }><h4>File size exceeded.</h4></div>
</div>
)
}
}
export default FileUpload;
I think you want to show the error message when file size exceeds given size
you can also use componentWillMount to call getfilesize
it actually depends document.getElementById("fileName")
it that particular element has populated the data by the time your parent componentWill Mount you can use componentWillMount lifecycle hook
Parent Component
class App extends Component {
constructor(props) {
super(props);
this.state = {};
}
onFileSelected(e) {
var fileSize = e.target.files.length > 0 ? e.target.files[0].size : false;
if (fileSize)
this.setState({ fileSize });
}
render() {
return (
<div className="App">
<input type="file" onChange={this.onFileSelected.bind(this)} />
<FileUpload fileSize={this.state.fileSize}></FileUpload>
</div>
);
}
}
child Component
class FileUpload extends Component {
render() {
return (
<div>
{this.props.fileSize > 100 ? <h2 >File size exceeds 100</h2> : null}
</div>
);
}
}
in the above code what i did is created <input type="file"/> in parent component and attached a onchange event to it.
Got it to work. Passed the function to the child:
<FileUpload fileSize={this.getFileSize.bind(this)} />
Then in the child added a setState to the form's onChange to call the function:
onChange={({ formData }) => { this.setState({ formData }); this.setState({fileSize:this.props.fileSize()})}}
and displayed the error message accordingly:
<div style={{display: this.state.fileSize > 100 ? "block": "none" }><h4>File size exceeded.</h4></div>
Related
I have a parent container which has a couple of child components. When the user clicks onClick={props.toggleReviewForm}, the function
toggleReviewForm () {
this.setState(prevState => ({
reviewFormActive: !prevState.reviewFormActive,
displayNameModalActive: !prevState.displayNameModalActive
}))
}
toggles the reviewForm state to visible. It's visibility is set with reviewFormActive={reviewFormActive} in the child component and the parent has `this.state = {reviewFormActive: false} set in the constructor. I am passing
displayNameModalActive={displayNameModalActive}
into the child component for the modal, but getting the error
Uncaught TypeError: Cannot read property 'displayNameModalActive' of undefined at DisplayNameModal.render
Parent Container
class ReviewsContainer extends React.Component {
constructor (props) {
super(props)
this.state = {
reviewFormActive: false,
displayNameModalActive: false
}
this.config = this.props.config
this.toggleReviewForm = this.toggleReviewForm.bind(this)
}
toggleReviewForm () {
this.setState(prevState => ({
reviewFormActive: !prevState.reviewFormActive,
displayNameModalActive: !prevState.displayNameModalActive
}))
}
render () {
const {
reviewFormActive,
displayNameModalActive
} = this.state
return (
<div className='reviews-container'>
<ReviewForm
config={this.config}
reviewFormActive={reviewFormActive}
toggleReviewForm={this.toggleReviewForm}
/>
{this.state.displayName &&
<div className='modal-container'>
<DisplayNameModal
bgImgUrl={this.props.imageUrl('displaynamebg.png', 'w_1800')}
config={this.config}
displayNameModalActive={displayNameModalActive}
displayName={this.state.displayName}
email={this.state.email} />
</div>
}
</div>
)
}
}
export default ReviewsContainer
Child Component (modal)
class DisplayNameModal extends React.Component {
constructor (props){
super(props)
this.state = {
displayName: this.props.displayName,
email: this.props.email.split('#')[0]
}
}
render (props) {
const {contentStrings} = this.props.config
return (
<div>
//Should only allow the modal to show if the username is the same as the email or there is no username available
{ props.displayNameModalActive && this.state.displayName === this.state.email || !this.state.displayName &&
<div className='display-name-container' style={{ backgroundImage: `url(${this.props.bgImgUrl})` }}>
<div className='display-name-content'>
<h2 className='heading'>{contentStrings.displayNameModal.heading}</h2>
<p>{contentStrings.displayNameModal.subHeading}</p>
<input type="text"
defaultValue={this.state.displayName}
placeholder={this.state.displayName}
minLength="3"
maxLength="15"/>
<button
onClick={this.updateDisplayName}
className='btn btn--primary btn--md'>
<span>{contentStrings.displayNameModal.button}</span>
</button>
<p className='cancel'>{contentStrings.displayNameModal.cancel}</p>
</div>
</div>
}
</div>
)
}
}
export default DisplayNameModal
Whyt this:
props.displayNameModalActive
and not this:
this.props.displayNameModalActive
?
Correct me if I'm wrong but render doesn't get props as argument.
I have a callback function in a parent component passed to an child component that should retrieve the input once submission in the child component takes place but is not working as expected. No data is being retrieved by the parent. Here is my code:
Parent:
class App extends Component {
constructor(props) {
super(props)
this.state = { item: '' }
}
getItem(item) {
this.setState({
item: item
})
console.log(this.state.item);
}
render() {
return (
<div className="App">
<Input getItem={this.getItem} />
<h2>{this.state.item}</h2>
</div>
);
}
}
Child:
class Input extends Component {
constructor(props) {
super(props)
this.state = { value: '' }
this.handleChange=this.handleChange.bind(this);
this.handleSubmit=this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({
value: e.target.value
})
console.log(this.state.value)
}
handleSubmit(e) {
{this.props.getItem(this.state.value)};
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
<input type="text" name={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="+" />
</form>
</div>
)
}
}
Solution 1 :
Use fat arrow in getItem function like this :
getItem = (item) => {
this.setState({
item: item
})
console.log(this.state.item);
}
Solution 2 :
Bind getItem function in counstructor like :
constructor(props) {
super(props)
this.state = { item: '' }
this.getItem = this.getItem.bind(this);
}
Solution 3 :
Bind getItem function in input directly :
<Input getItem={this.getItem.bind(this)} />
You should bind the getItem context to the App class.
You can do this by doing
<Input getItem={this.getItem.bind(this)} />
or by using the arrow function when you define getItem
or by binding the method in the constructor as you did for the child component
I am sending a callback function from a parent to a child component, and although the parent successfully updates it's state based on input provided to the child, it immediately reverts back to initial state, thus resulting in the browser briefly flashing the input that was provided and then displaying the initial state. What is the fix for this? Here is my code:
Parent:
class App extends Component {
constructor() {
super()
this.state = { item: '' }
this.getItem=this.getItem.bind(this);
}
getItem(val) {
this.setState({
item: val
})
console.log(this.state.item);
}
render() {
return (
<div className="App">
<Input getItem={this.getItem} />
<h2>{this.state.item}</h2>
</div>
);
}
}
Child:
class Input extends Component {
constructor(props) {
super(props)
this.state = { value: '' }
this.handleChange=this.handleChange.bind(this);
this.handleSubmit=this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({
value: e.target.value
})
console.log(this.state.value)
}
handleSubmit(e) {
{this.props.getItem(this.state.value)};
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
<input type="text" name={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="+" />
</form>
</div>
)
}
}
I was able to solve this by using e.preventDefault() in the handleSubmit function.
The aim of my application is to get some array of messages(mes) from api-url and send answer(subFunction method) for every message. After that message will be delete from 'mes' array. 'Message' is a parent component responsible for fetch data(componentDidMount event) and rendering message through map method. 'MessageItem' responsible for get value from 'textarea' - the body of answer. But I can't transfer this.state.value(textarea.value) from from MessageItem to parent component. If I place 'subFunction' in child component, I can't change this.state.mes
import React from 'react'
import ReactDOM from 'react-dom'
const url="api-url";
class MessageItem extends React.Component {
constructor(props) {
super(props);
this.state = {
value:'',
};
};
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
return (
<div className="message_wrap" key={this.props.message_id}>
<div className="message_body">
{this.props.message_body}
</div>
<div className="input-field col s12">
<textarea value={this.state.value} onChange={this.handleChange.bind(this)}/>
<label htmlFor="textarea1">
Ответ
</label>
<button onClick={this.props.onClick}>
Отправить
</button>
</div>
</div>
);
}
}
class Message extends React.Component {
constructor(props) {
super(props);
this.state = {
mes:[],
};
};
componentDidMount(){
fetch(url).then(function(response){
return response
}).then(function (response) {
return response.json()
}).then((data)=>{
this.setState({mes:data})
})
}
subFunction(user_id, value) {
/*This method have to send answer with user id and textarea value*/
}
render() {
return (
<div>
{this.state.mes.map((index)=>
(
<MesItem
key={index.message_id}
message_body={index.message_body}
onClick={this.subFunction.bind(this, index.user_id)}
/>
)
)
}
</div>
);
}
}
ReactDOM.render(<Message/>, document.getElementById('container'));
You are passing a function from parent component to child, call that function to pass the value from child to parent.
Like this:
<button onClick={() => this.props.onClick(this.state.value)}>
Отправить
</button>
Now do console.log inside subFunction it will print proper value:
subFunction(user_id, value) {
console.log(user_id, value)
}
Assign a ref to the textarea in the child component and to the MesItem and then you can fetch the value like
class MessageItem extends React.Component {
constructor(props) {
super(props);
this.state = {
value:'',
};
mesItem = [];
};
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
return (
<div className="message_wrap" key={this.props.message_id}>
<div className="message_body">
{this.props.message_body}
</div>
<div className="input-field col s12">
<textarea value={this.state.value} ref={(ta) => {this.text = ta}}onChange={this.handleChange.bind(this)}/>
<label htmlFor="textarea1">
Ответ
</label>
<button onClick={this.props.onClick}>
Отправить
</button>
</div>
</div>
);
}
}
class Message extends React.Component {
constructor(props) {
super(props);
this.state = {
mes:[],
};
};
componentDidMount(){
fetch(url).then(function(response){
return response
}).then(function (response) {
return response.json()
}).then((data)=>{
this.setState({mes:data})
})
}
subFunction(user_id, i) {
console.log(this.mesItem[i].text.value)
}
render() {
return (
<div>
{this.state.mes.map((index, i)=>
(
<MesItem
ref = {(ip) => {this.mesItem[i] = ip}}
key={index.message_id}
message_body={index.message_body}
onClick={this.subFunction.bind(this, index.user_id , i)}
/>
)
)
}
</div>
);
}
}
ReactDOM.render(<Message/>, document.getElementById('container'));
I'm trying to create a component that will print out input text to the screen, here is what I'm working on.
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = { term: '' };
}
render() {
return (
<div className="search-bar">
<input value={this.state.term} onChange={event => this.SetState(event.target.value)} />
The value of input is: {this.state.term}
</div>
);
}
}
However I keep getting an error in Chrome console:
bundle.js:19818 Uncaught TypeError: _this2.SetState is not a function
Any ideas?
Thanks
Try this:
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = { term: '' };
this.setInputState = this.setInputState.bind(this);
}
setInputState(event) {
this.setState({ term: event.target.value });
}
render() {
return (
<div className="search-bar">
<input value={this.state.term} onChange={this.setInputState} />
The value of input is: {this.state.term}
</div>
);
}
}
you have to bind {event => this.SetState(event.target.value)} function to component this. problem is your onChange function not running your component this context
code should look something like this
const onChange = (event) => { this.setState({term:event.target.value}) }
<input value={this.state.term} onChange={onChange.bind(this) />
I think you need to bind your this, try this (no pun intended).
render() {
return (
<div className="search-bar">
<input value={this.state.term} onChange={event => this.setState.bind(this, event.target.value)} />
The value of input is: {this.state.term}
</div>
);
}
I believe you had to specify what was changed in the state:
this.setState({ term: event.target.value });
instead of
this.setState( event.target.value );
Try this code:
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = { term: '' };
}
render() {
return (
<div className="search-bar">
<input value={this.state.term} onChange={event => this.setState({term:event.target.value})} />
The value of input is: {this.state.term}
</div>
);
}
}
This worked for me. I think you missed out on the state key name term and also this SetState, which should be setState. Just follow my example below am sure it will work for you also.
class Square extends React.Component {
constructor(props){
super(props)
this.state = {
value: null
};
}
render() {
return (
<button
className="square"
onClick={()=> this.setState({value:'X'})}>
{this.state.value}
</button>
);
}
}
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = { term: '' };
}
render() {
return (
<div className="search-bar">
<input value={this.state.term} onChange={event => this.SetState(event.target.value)} />
The value of input is: {this.state.term}
</div>
);
}
}
you wrote SetState instead of
I faced a similar problem and noticed that I was using this.state.setState() method instead of this.setState().
Although OP's problem is the wrong capitalization.