Getting value from react component - javascript

I have a component InputArea with state = {input: ''}
Then I map several of these components in a container and write them in state = {inputAreas: []}
Now, how can I get inputs in the container? Logging this.state.inputAreas[0] returns object like this:
{$$typeof: Symbol(react.element), type: ƒ, key: "1", ref: null, props:
{…}, …}
In elements it shows like this:
<input type="text" class="form-control" name="input" value="abc">
Using this.state.prefooterArea[0].value gives undefined.
I also tried passing input from component to container as props, but it says getInput is not a function. From what I understood it has something to do with the fact I used map in the container. I can't use redux in this project.
Code of component
class PrefooterAreaInput extends Component {
state = {
input: ''
}
textChangedHandler = (event) => {
let newState = {};
newState[event.target.name] = event.target.value;
this.setState(newState);
}
render() {
return (
<div>
<input
className="form-control"
type="text"
name="input"
value = {this.state.input}
onChange={this.textChangedHandler}
/>
</div>
)
}
}
Code of container
class DescriptionFrame extends Component {
state = {,
prefooterArea: [<PrefooterAreaInput key={1}/>]
};
addFooterInputHandler = event => {
event.preventDefault();
if (this.state.prefooterArea.length < prefooterInputFieldsMax) {
var newPrefooterArea = this.state.prefooterArea.map(
inputField => inputField
);
newPrefooterArea.push(
<PrefooterAreaInput key={this.state.prefooterArea.length + 1} />
);
this.setState({ prefooterArea: newPrefooterArea });
}
};
removeFooterInputHandler = event => {
event.preventDefault();
if (this.state.prefooterArea.length > 1) {
var newPrefooterArea = this.state.prefooterArea.map(
inputField => inputField
);
newPrefooterArea.splice(newPrefooterArea.length - 1);
this.setState({ prefooterArea: newPrefooterArea });
}
render() {
// want to get this.state.prefooterArea[0]'s value
return (
<div>
{this.state.prefooterArea}
<a
className="nav-link"
href=""
onClick={this.addFooterInputHandler}
>
Add More
</a>
<a
className="nav-link"
href=""
onClick={this.removeFooterInputHandler}
>
Remove Last
</a>
</div>
);
}
}

Figured it out. This caused problem.
prefooterArea: [<PrefooterAreaInput key={1}/>]
I should have added that initial PrefooterAreaInput with lifecycle method instead. With that I was able to pass state just fine.

Are you trying to achieve something like this ?
child component :
export default class InputBox extends React.Component {
render() {
return (
<input onChange={event => this.props.onChange(event.target.value)} />
);
}}
parent component :
import InputBox from './InputBox';
class FilterBar extends React.Component {
constructor(props) {
super(props);
this.state = {
inputs: "" //get input value from state this input
};
this.updateFilters = this.updateFilters.bind(this);
}
updateFilters(i) {
this.setState({ inputs: i }); // this will print whatever input you type
}
render() {
return (
<div>
<InputBox onChange={(i) => this.updateFilters(i)} />
</div>
);
}
}

Related

React App class won't render when a child Component changes

I just started to learn React. I'm trying to write a Todo list and so far it looks like:
However when I check the box of a Todo, the count of things left to do won't change even when the state of a list of Todos changes (the 'checked' property of a Todo that I just checked change to true)
My App.js:
import React, {Component} from 'react';
import TaskComponent from "./TaskComponent";
class App extends Component {
constructor(props) {
super(props)
this.state = {
taskList: [],
newTaskContent: ''
}
this.generateTask = this.generateTask.bind(this)
this.updateNewTaskContent = this.updateNewTaskContent.bind(this)
}
generateTask() {
if (this.state.newTaskContent) {
const joined = this.state.taskList.concat({
id: this.state.taskList.length + 1,
content: this.state.newTaskContent,
checked: false
})
this.setState({taskList: joined, newTaskContent: ''})
}
}
updateNewTaskContent({target: {value}}) {
this.setState({newTaskContent: value})
}
render() {
return (
<div>
<ul>{this.state.taskList.map(task => <TaskComponent key={task.id} task={task.content}
checked={task.checked}/>)}</ul>
<input type='text' placeholder='Type your new task'
onChange={this.updateNewTaskContent} value={this.state.newTaskContent}/>
<button name='generateTask' onClick={this.generateTask}>Generate task</button>
<div>There are {this.state.taskList.filter(task => !task.checked).length} things left to do!</div>
</div>
);
}
}
export default App;
My TaskComponent.js file:
import React, {Component} from 'react'
class TaskComponent extends Component {
constructor({task, checked}) {
super(undefined)
this.state = {
taskContent: task,
checkedState: checked
}
this.changeHandler = this.changeHandler.bind(this)
}
changeHandler({target: {checked}}) {
this.setState({checkedState: checked})
}
render() {
return (
<div>
<span>{this.state.taskContent}</span>
<input type="checkbox" checked={this.state.checkedState} onChange={this.changeHandler}/>
</div>
);
}
}
export default TaskComponent;
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
taskList: [],
newTaskContent: ''
}
this.generateTask = this.generateTask.bind(this)
this.updateNewTaskContent = this.updateNewTaskContent.bind(this)
}
generateTask() {
if (this.state.newTaskContent) {
const joined = this.state.taskList.concat({
id: this.state.taskList.length + 1,
content: this.state.newTaskContent,
checked: false
})
this.setState({taskList: joined, newTaskContent: ''})
}
}
updateNewTaskContent({target: {value}}) {
this.setState({newTaskContent: value})
}
render() {
return (
<div>
<ul>{this.state.taskList.map(task => <TaskComponent key={task.id} task={task.content}
checked={task.checked}/>)}</ul>
<input type='text' placeholder='Type your new task'
onChange={this.updateNewTaskContent} value={this.state.newTaskContent}/>
<button name='generateTask' onClick={this.generateTask}>Generate task</button>
<div>There are {this.state.taskList.filter(task => !task.checked).length} things left to do!</div>
</div>
);
}
}
class TaskComponent extends React.Component {
constructor({task, checked}) {
super(undefined)
this.state = {
taskContent: task,
checkedState: checked
}
this.changeHandler = this.changeHandler.bind(this)
}
changeHandler({target: {checked}}) {
this.setState({checkedState: checked})
}
render() {
return (
<div>
<span>{this.state.taskContent}</span>
<input type="checkbox" checked={this.state.checkedState} onChange={this.changeHandler}/>
</div>
);
}
}
ReactDOM.render(<App />, document.querySelector("#root"));
<script src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Inside the TaskComponent class I add the function for the event of changing the "checked" state of the check box but somehow the 'taskList' state in my App does not change at all when I try to console.log it. What is my problem here? Be gentle since I'm new to React and Javascript in general.
You are setting state in TaskComponent and expecting it to change the prop in App.js.
Instead of setting a TaskComponents's state when it is checked, I would recommend calling a function passed in as a prop when it is checked, which has its id and new value. Something along the lines of:
App.js:
// somewhere in your class:
handler(id, value) {
// set state to reflect changes
}
// in your render()
<ul>{this.state.taskList.map((task) => {
<TaskComponent onChange={this.handler} id={task.id} key={task.id} task={task.content} checked={task.checked} />})}</ul>
In TaskComponent.js:
changeHandler({target: {checked}}) {
this.props.onChange(this.props.id, checked);
}
I would also recommend making TaskComponent not have state at all, because it seems unnecessary to me.

Conditional rendering on select

I am pretty new to the wonderful world of React.
I have two inputs passing data through from an API that renders a list of options. And I want to send the selected inputs from those options back to the parent in the input fields to display for another search.
I have tried passing state down to them and render them them optionally with both a ternary and an if else statement in the "SearchCityList" component in several ways but I either get both lists rendered and they would have to choose between one list that is doubled to put in each input field or it only puts the selected value in one input. Would appreciate any & all suggestions Thanks!
class Form extends Component {
state = {
showComponent: false,
showComponent2: false,
};
// open/close control over SearchCity component box
openSearch = () => {
this.setState({ showComponent: true });
};
openSearch2 = () => {
this.setState({ showComponent2: true });
};
closeSearch = () => {
this.setState({
showComponent: false,
showComponent2: false
});
};
// Passed down cb function to get selected city search in selectCity component
GoingTo = (flights) => {
this.setState({ GoingTo: [flights] });
};
LeavingFrom = (flights) => {
this.setState({ LeavingFrom: [flights] });
};
render() {
return (
<div>
<form className="form-fields container">
<div className="inputs">
<h1>Search for a flight!</h1>
<div className="depart">
<input
onClick={this.openSearch}
className="flight-search"
placeholder="Leaving From"
value={this.state.LeavingFrom}
></input>
<input type="date"></input>
</div>
<div className="Returning">
<input
onClick={this.openSearch2}
className="flight-search"
placeholder="Going To "
value={this.state.GoingTo}
></input>
<input type="date" placeholder="Returning"></input>
</div>
</div>
<button>Check Flights!</button>
</form>
{this.state.showComponent || this.state.showComponent2 ? (
<SearchCity
openSearch={this.openSearch}
openSearch2={this.openSearch2}
flightSearch={this.state.flightSearch}
closeSearch={this.closeSearch}
GoingTo={this.GoingTo}
LeavingFrom={this.LeavingFrom}
onSearchSubmission={this.onSearchSubmission}
closeSearch={this.closeSearch}
/>
) : null}
</div>
);
}
}
export default Form;
class SearchCity extends Component {
state = {
LeavingFrom: "",
GoingTo: "",
search: "",
flightSearch: [],
};
// Search submission / api call
onSearchSubmission = async (search) => {
const response = await Axios.get(
{
headers: {
"
useQueryString: true,
},
}
);
// set New state with array of searched flight data sent to searchCity component
const flightSearch = this.setState({ flightSearch: response.data.Places });
};
// Callback function to send search/input to parent "Form" component
submitSearch = (e) => {
e.preventDefault();
this.onSearchSubmission(this.state.search);
};
// closeSearch callback function sent from Form component to close pop up search box when X is pressed
closeSearch = () => {
this.props.closeSearch();
};
render() {
return (
<div className="container search-list">
<form onChange={this.submitSearch}>
<i className="fas fa-times close-btn" onClick={this.closeSearch}></i>
<input
onChange={(e) => this.setState({ search: e.target.value })} //query-search api
value={this.state.search}
className="search-input"
type="text"
placeholder="Search Locations"
></input>
<div className="search-scroll">
<SearchCityList
openSearch={this.props.openSearch}
openSearch2={this.props.openSearch2}
LeavingFrom={this.props.LeavingFrom}
GoingTo={this.props.GoingTo}
flightSearch={this.state.flightSearch}
/>
</div>
</form>
</div>
);
}
}
export default SearchCity;
function SearchCityList({ flightSearch, LeavingFrom, GoingTo }) {
const renderList = flightSearch.map((flights) => {
return (
<div>
<SelectCityLeaving LeavingFrom={LeavingFrom} flights={flights} />
<SelectCityGoing GoingTo={GoingTo} flights={flights} />
</div>
);
});
return <div>{renderList}</div>;
}
export default SearchCityList;
First of all, when dealing with state, make sure you initialize in the constructor and also ensure you bind your handlers to this component instance as this will refer to something else in the handlers if you don't and you won't be able to call this.setState().
constructor(props) {
super(props); // important
state = {
// your state
};
// make sure to bind the handlers so `this` refers to the
// component like so
this.openSearch = this.openSearch.bind(this);
}

Get prop value from div in React

A working example of my problem can be found at:
https://codepen.io/RyanCRickert/pen/vYYQeaW
I am prop drilling a function two levels and passing that function along with an index to a rendered component. When a name is submitted it renders a new component which shows the name and div which has an onClick (X). I am trying to receive the index of where the name is located in the array which it lives so that I may splice it out when the button is clicked.
If I enter the name "Bob" for example, then click the div with the listener I can console log the event.target. Using the above example I get "<div class='person-item__X' value='0'>X</div>" for event.target and undefined for event.target.value. The value is being assigned as <div onClick={props.removeName} class="person-item__X" value={props.value}>X</div>.
Am I just unable to grab the value of a div in such a manor? Or is there something that I am missing? Thank you
Change these to your code
const PersonListItem = props => (
<div class="person-item">
<div class="person-item__name">{props.name}</div>
<div onClick={() => props.removeName(props.value)} class="person-item__X" value={props.value}>X</div>
</div>
);
Inside PeopleList replace this line
<PersonListItem key={index} name={person} value={index} removeName={(id) => props.removeName(id)} />
Inside TeamGenerator replace this line
<PeopleList people={this.state.names} removeName={(id) => this.handleRemoveName(id)} />
now in handleRemoveName you will recieve a id of the item on which X was clicked
handleRemoveName = id => {
const currentArr = this.state.names;
console.log(id);
}
In your case, to grab the value inside this div, you should use ref API.
Your code should look like this:
TeamGenerator.js
import React, { Component } from "react";
import CustomModal from "./Modal";
import PeopleList from "./PeopleList";
import "./index.css";
export default class App extends Component {
constructor(props) {
super(props);
// Create a ref
this.divTextRef = React.createRef();
this.state = {
names: [],
selectedName: ""
};
}
handleCloseModal = () => {
this.setState({
selectedName: ""
});
};
handleChange = e => {
this.setState({ name: e.target.value });
};
handleRemoveName = index => {
// Get your name and index this way
console.log("Your text: ", this.divTextRef.current.innerHTML);
console.log("Your index: ", index);
};
handleSubmit = e => {
e.preventDefault();
const currentNames = this.state.names;
if (this.state.name)
currentNames.push(
this.state.name[0].toUpperCase() + this.state.name.slice(1)
);
this.setState({
name: "",
names: currentNames
});
};
render() {
return (
<div className="container">
<CustomModal
selectedName={this.state.selectedName}
closeModal={this.handleCloseModal}
/>
<form onSubmit={this.handleSubmit}>
<label>
Add name:
<input
type="text"
value={this.state.name}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
<div className="people-list-container">
<PeopleList
people={this.state.names}
removeName={this.handleRemoveName}
upperRef={this.divTextRef} // Pass the ref down from your Component tree
/>
</div>
</div>
);
}
}
PeopleList.js
import React from "react";
import PersonListItem from "./PersonListItem";
export default class PeopleList extends React.Component {
render() {
return (
<div className="people-container">
<div className="people-title">List of people</div>
<div className="people-list">
{this.props.people.length === 0 ? (
<div className="people-item">
<span>No people added</span>
</div>
) : (
this.props.people.map((person, index) => (
<PersonListItem
key={index}
name={person}
value={index}
removeName={() => this.props.removeName(index)} // Passing index to the removeName function of Parent
upperRef={this.props.upperRef} // Continue passing it down to PersonListItem
/>
))
)}
</div>
</div>
);
}
}
PersonListItem.js
import React from "react";
const PersonListItem = props => (
<div className="person-item">
<div ref={props.upperRef} className="person-item__name"> // Use the passed ref
{props.name}
</div>
<div
onClick={props.removeName}
className="person-item__X"
value={props.value}
>
X
</div>
</div>
);
export default PersonListItem;
The div node does not have the value like input, so you can not grab it by your old way.

React - how to pass multiple input values to child in on change event when values have different types (is not always e.target.value)?

I have multiple input fields and 1 react-select dropdown field. I created a method in my parent component that sets the state with the values from the input, passes it down to the child which should call the method. My problem is that react-select doesn't take the value but an object like this:
{value: 'xy', name:'x', label: 'y'}
so normally my function in my onChange event handler would look like this (when passing multiple values):
in parent:
testing(e) {
this.setState({
[e.target.name]: e.target.value
})
}
in child:
<input type="text" name="maxfare" onChange={this.onChange}/>
...
onChange(e){
var value = [e.target.name] = e.target.value;
this.props.onChange(value);
}
...
However, while my input fields take:
e.target.value
my select dropdown takes entire 'e' - not e.target.value. I tried to pass my onChange function in child component 2 arguments, calling my method in parent with 2, but that doesn't to work. Any help would be great! My code is below (the relevant parts- if I forgot something that you think is important, please let me know). Ps. I thought about having 2 onChange functions, passing once my value for select dropdown and a second one doing the rest, but then I would need to pass 2 onChange methods to the child and I believe thats not possible in react?! Thanks!!:
Parent:
...
onChangeT(selectValue, value) {
this.setState({
origin: selectValue,
maxfare: value
...
})
}
render(){
....
<Parent cities={this.state.citiesToSelect} origin={this.state.origin} maxfare={this.state.maxfare} onChange={this.onChangeT}/>
...
}
Child:
....
onChangeC(e){
var value = [e.target.name] = e.target.value;
this.props.onChange(e, value);
console.log("name", name)
}
....
<Select
onChange={this.onChangeC}
labelKey='name'
value={this.props.origin}
options={this.props.cities}
/>
<input type="text" name="maxfare" onChange={this.onChangeC}/>
We want to be able to do this in the parent
onChange = (name, value) => {
this.setState({[name]: value});
}
We fix the "wiring" of the children onChange to do exactly that, raise an onChange with a name and a value. Wrap react-select and provide a consistent interface to the parent.
Form example
import * as React from 'react';
import Input from './Input';
import Select from './Select';
export default class Form extends React.Component {
state = {
input: '',
select: '',
options: ['A', 'B', 'C']
};
onChange = (name: string, value: string) => {
this.setState({[name]: value});
}
render() {
return (
<form>
<Input
label="Surname"
name={'input'}
value={this.state.input}
onChange={this.onChange}
/>
<Select
label="Grade"
name={'select'}
value={this.state.select}
options={this.state.options}
onChange={this.onChange}
/>
</form>
);
}
}
Input example
import * as React from 'react';
export default class Input extends React.Component {
onChange = (e) => {
const {onChange, name} = this.props;
if (onChange) {
onChange(name, e.currentTarget.value);
}
}
render() {
return (
<div>
<label>{this.props.label}</label>
<input
type="text"
name={this.props.name}
value={this.props.value}
onChange={this.onChange}
/>
</div>
);
}
}
And a DOM native <Select /> example
import * as React from 'react';
export default class Select extends React.Component {
onChange = (e) => {
const {onChange, name} = this.props;
if (onChange) {
onChange(name, e.currentTarget.value);
}
}
render() {
return (
<div>
<label>{this.props.label}</label>
<select
name={this.props.name}
value={this.props.value}
onChange={this.onChange}
>
{this.props.options.map(o => <option key={o}>{o}</option>)}
</select>
</div>
);
}
}
The fact that react-select doesn't return a native event nor a similar object shape of a native event, is forcing you to normalize the shape of the object that returned from it. You can do that by wrapping the Select component of react-select with your own component and returning a custom object for your use-case.
In this example we are trying to normalize the behavior of our onChange event both for inputs and Select. We will first check if the object that returned is having a target key, if it does we know that this is a native event that we are handling and we will set the state according to the name of the input and its value (exactly how you did it in your example).
If we don't have a target key, then we may handle a different kind of event.
We will check if we get a selectedValue key (just a convention between yourself, you can change the key as you like), then we will set the state by its name and selectedValue that we received.
This will only work if you will pass the name upwards of course.
So the object that you need to return from the custom Select component should look something like this:
{name: this.props.name, selectedValue }
// where selectedValue is the object received from the real Select component
Here is a running example:
const options = [
{ value: 'one', label: 'One' },
{ value: 'two', label: 'Two' },
]
const moreOptions = [
{ value: 'mike', label: 'johnson' },
{ value: 'lynda', label: 'bog' },
]
class MySelect extends React.Component {
handleChange = selectedValue => {
const { name, onChange } = this.props;
onChange({ name, selectedValue });
}
render() {
const { options, value, ...rest } = this.props;
return (
<Select
{...rest}
value={value}
onChange={this.handleChange}
options={options}
/>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
option1: '',
option2: '',
value1: 1,
value2: '',
value3: 3,
}
}
handleChange = e => {
let nextState;
if (e.target) {
const { name, value } = e.target;
nextState = { [name]: value };
} else if (e.selectedValue) {
const { name, selectedValue } = e;
nextState = { [name]: selectedValue };
}
this.setState(nextState);
}
render() {
const { value1, value2, value3, option1, option2 } = this.state;
return (
<div>
<MySelect
value={option1.value}
onChange={this.handleChange}
options={options}
name="option1"
/>
<div>
<span>input1 </span>
<input value={value1} name="value1" onChange={this.handleChange} />
</div>
<div>
<span>input2 </span>
<input value={value2} name="value2" onChange={this.handleChange} />
</div>
<div>
<span>input3 </span>
<input value={value3} name="value3" onChange={this.handleChange} />
</div>
<MySelect
value={option2.value}
onChange={this.handleChange}
options={moreOptions}
name="option2"
/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<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>
<script src="https://unpkg.com/prop-types#15.5.10/prop-types.js"></script>
<script src="https://unpkg.com/classnames#2.2.5/index.js"></script>
<script src="https://unpkg.com/react-input-autosize#2.0.0/dist/react-input-autosize.js"></script>
<script src="https://unpkg.com/react-select/dist/react-select.js"></script>
<link rel="stylesheet" href="https://unpkg.com/react-select/dist/react-select.css">
<div id="root"></div>

The '.onChange' method doesn't get the newest info of 'Input' in React.js

import reqwest from './public/reqwest.js'
const PAGE_SIZE = 10
class App extends Component {
constructor() {
super()
this.state = {
page: 1,
arr: []
}
}
singleInfo(page) {
reqwest({
url: 'https://cnodejs.org/api/v1/topics',
data: {
limit: PAGE_SIZE,
page: this.state.page
},
success: function (data) {
this.setState({
arr: data.data
})
}.bind(this)
})
}
changeState(newState) {
this.setState(newState)
this.singleInfo(newState.page)
}
render() {
return (
<div>
<Menu val={this.state.page} changeParentState={(state) => this.changeState(state)} />
<List arr={this.state.arr} />
</div>
);
}
}
class Menu extends Component {
handleChange(event) {
if(event.target.value) {
this.props.changeParentState({
page: event.target.value
})
}
}
render() {
console.log(this)
return <input type="text" defaultValue={this.props.val} onChange={(event) => this.handleChange(event)} />
}
}
class List extends Component {
render() {
return <ul>
{
this.props.arr.map((ele) => {
return (
<li key={ ele.id }>
<p className="title">{ ele.title }</p>
<p className="date">{ ele.create_at }</p>
<p className="author">{ ele.author.loginname }</p>
</li>
)
})
}
</ul>
}
}
I can't get the current value of the input by onChange in Menu module.
In my code, the App has two child components - List & Menu.
You can input the page in Menu component, so it will send Ajax() to get the info of the page. But the truth is: After I change the value of input like 1 -> 10, the ajax get the value of 1.
Before this, I know the difference between keyup or keydown and keypress. They have the difference cross browser. But I just want get the current value of the input By React.js.
First, change:
<input type="text" defaultValue={this.props.val} onChange={(event) => this.handleChange(event)} />
To:
<input type="text" value={this.props.val} onChange={(event) => this.handleChange(event)} />
so that your input will update to the correct value on re-render.
Second, remember that setState is often asynchronous. So do not expect the state to be changed right after calling setState.
Your changeState method is good in this respect since it passes newState to singlePageRequest. However singlePageRequest does not use the supplied value and instead uses this.state.page. Change it to use the supplied value and you should be OK:
singleInfo(page) {
reqwest({
url: 'https://cnodejs.org/api/v1/topics',
data: {
limit: PAGE_SIZE,
page: page
},
success: function (data) {
this.setState({
arr: data.data
})
}.bind(this)
})
}

Categories

Resources