How can I get <textarea> value in parent component from child? - reactjs

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'));

Related

Why is this.setState not updating in my click function?

I am trying to update my state by using a click function. However for some reason it is not updating. Could someone please explain to me what I am doing wrong?class Textbox extends
Component {
constructor(props) {
super(props);
this.handle = this.handle.bind(this);
this.state = {
text: 'jkjkljkljl'
}
}
handle(event) {
const myValue = event.target.value;
this.setState({
text: myValue
})
console.log(this.state)
}
render() {
return (
<div>
<textarea className="Textbox" rows="2" cols="30" type = "text" >
</textarea>
<button className="postbutton" onClick={this.handle.bind(this)}>Post</button>
<h1>{this.state.text}</h1>
</div>
);
}
}
export default Textbox;
Here is an updated version of your code that works.
Issue was that you were trying to set the value of the button to the state.
What you should do is setup textarea as a controlled input (have value and onChange setup as I did below) and use that value on click.
class Component extends React.Component {
constructor(props) {
super(props);
this.state = {
textArea: "",
text: "jkjkljkljl"
};
}
handle(event) {
console.log(event);
this.setState({
text: this.state.textArea
});
console.log(this.state);
}
handleChange(event) {
this.setState({ textArea: event.target.value });
}
render() {
return (
<div>
<textarea
className="Textbox"
rows="2"
cols="30"
value={this.state.textArea}
onChange={this.handleChange.bind(this)}
/>
<button className="postbutton" onClick={this.handle.bind(this)}>
Post
</button>
<h1>{this.state.text}</h1>
</div>
);
}
}
It seems you are trying to handle a form using React/JSX. There are great libraries for this purpose (React Forms).
This is the proper code:
class App extends React.Component {
constructor(props) {
super(props);
this.handle = this.handle.bind(this);
this.state = {
text: 'Static'
}
}
handleOnChange(event) {
this.setState({text: event.target.value});
}
handleSubmit(event) {
if (event.keyCode == 13) return this.sendData();
}
render() {
return (
<div>
<form onKeyUp={this.handleOnChange}>
<textarea className="Textbox"
rows="2" cols="30" type="text"
>
</textarea>
<button className="postbutton"
onClick={this.handleSubmit.bind(this)}>
Post
</button>
</form>
<h1>{this.state.text}</h1>
</div>
);
}
}
React.render(<App />, document.getElementById('app'));
In your example, you are binding the state to the root of the button and not the textarea. If you want a static example (whereas the above code changes as you type), you may simply handle the enter key via if (event.keyCode == 13) return this.sendData() and remove the onChange.

Toggle modal in React

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.

Parent not Successfully Retrieving Data from Child

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

React - pass dynamic value to child

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>

React Passing state to sibling component and up to parent class

Very very new to React and I seem to be stuck. This is a simple Todo app, I basically have 3 components, the base component, an input component and a task component. I have figured out how to edit the state within each component but I am having trouble passing state from component to component.
class App extends Component {
render() {
return (
<div id="appContainer">
<HeaderTitle />
<TaskInput />
<Task taskState={true} text="task one" />
<Task taskState={true} text="task two" />
<Task taskState={true} text="task three" />
</div>
);
}
}
class TaskInput extends React.Component {
constructor(props) {
super(props);
this.state = {}
}
update(e) {
this.setState({inputValue: e.target.value});
console.log(this.state);
}
taskCreate(e) {
this.setState({text: this.state.inputValue, completeState: false});
console.log('button clicked');
console.log(this.state);
}
render () {
return (
<div className="taskInputContainer">
<TaskInputField update={this.update.bind(this)} taskCreate={this.taskCreate.bind(this)} />
</div>
)
}
}
class Task extends Component {
constructor(props) {
super();
this.state = {
completeState: false
}
}
toggleTask (e) {
this.setState({
completeState: !this.state.completeState
});
}
delete (item) {
}
render() {
return (
<div className="taskContainer" onClick={this.toggleTask.bind(this)}>
<div className={"taskState " + this.state.completeState}></div>
<div className={"taskText " + this.state.completeState }>{this.props.text}</div>
<div className="taskDelete"><i className="fa fa-times-circle-o" aria-hidden="true"></i></div>
</div>
);
}
}
const TaskInputField = (props) =>
<div className="taskInputContainer">
<input type="text" className="taskInputField" onChange={props.update}/>
<i className="fa fa-plus-circle" aria-hidden="true" onClick={props.taskCreate}></i>
</div>;
Task.propTypes = {
text: PropTypes.string.isRequired,
completeState: PropTypes.bool
};
Task.defaultProps = {
text: 'Task',
completeState: false
};
const HeaderTitle = () => (
<h1>Davids Todo List</h1>
);
export default App;
So in the TaskInput has its own state that I can update but how do I pass that up to the parent component to update and add a Task component? Also how do I add a Task component without re-rendering the whole thing?
This issue is documented in detail in the article 'lifting the state up' in React's documentation.
TLDR, you create a handler that updates the state of the current component and pass it to children as props. In the example below (a modified version of your code), I passed down the methods that changes the state of component App, into its children components (TaskInput and Tasks).
class App extends React.Component {
constructor() {
super();
this.state = {
tasks: [],
}
}
addTask = (e, text) => {
e.preventDefault();
const newTask = {
id: new Date().getTime(),
done: false,
text
};
const newTasks = this.state.tasks.concat([newTask]);
this.setState({
tasks: newTasks
})
}
toggleTask = (id) => {
const updatedTask = this.state.tasks.filter(task => task.id === id);
updatedTask[0].done = !updatedTask[0].done;
const newTasks = this.state.tasks.map(task => {
if (task.id === id) {
return updatedTask[0];
}
return task;
});
this.setState({
tasks: newTasks
});
}
render() {
return (
<div id="appContainer">
<HeaderTitle />
<TaskInput addTask={this.addTask} />
{
this.state.tasks.length > 0 ? <Tasks tasks={this.state.tasks} toggleTask={this.toggleTask}/> : <div>no tasks yet</div>
}
</div>
);
}
}
class TaskInput extends React.Component {
constructor(props) {
super(props);
this.state = {
currentInput: ''
}
}
handleChangeText = (e) => {
this.setState({
currentInput: e.target.value,
})
}
render() {
return (<form>
<input type="text" value={this.state.currenInput} onChange={this.handleChangeText}/><input type="submit" onClick={(e) => this.props.addTask(e, this.state.currentInput)} value="Add Task"/></form>)
}
}
const Tasks = (props) => (
<div>
{
props.tasks.map(task => (
<div
style={ task.done ? { textDecoration: 'line-through'} : {} }
onClick={() => props.toggleTask(task.id)}
>{task.text}</div>
))
}
</div>
);
const HeaderTitle = () => (
<h1>Davids Todo List</h1>
);
ReactDOM.render(<App />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Resources