React: Accessing child component function but gives an undefined error - javascript

So basically I'm trying to access a function in a child node (that returns the input of a text-box), from the parent node but i keep on getting an error stating that the function is not a function.
MakeMethod.jsx:22 Uncaught TypeError: i.getState is not a function
at MakeMethod.jsx:22
at Array.map (<anonymous>)
at MakeMethod.getAllInfo (MakeMethod.jsx:22)
at onClick (MakeMethod.jsx:41)
I've tried using the ref prop but it doesn't seem to work with an array of components.
The methods/function used to return the props parameter from the child node is the getState() one.
The method/function used to store the list of inputs from the children is the getAllInfo() one.
Is there something I'm doing drastically wrong? If so Would you be able to point it out to me?
Many thanks in advance!!
The parent class is the MakeMethod:
import React from 'react';
import "./MakeRecipe.css";
import Steps from "./Steps.jsx";
const count=1;
const theSteps=[]; //list of step components i want to access
class MakeMethod extends React.Component {
constructor(){
super();
this.state = {
count : 1,
}
this.AddMethod = this.AddMethod.bind(this);
this.getAllInfo=this.getAllInfo.bind(this);
theSteps.push(<Steps key={this.state.count} count={this.state.count} value={''}/>);
}
//this is the function i want to access all my information from
getAllInfo(){
let method=[];
method=theSteps.map((i)=>
i.getState() //iterates through each Step component and calls on the function getState()
//but it says that the function does not exist
);
this.setState({steps:method});
console.log(method);
}
AddMethod(){
let x=this.state.count+1;
theSteps.push(<Steps key={x} count={x}/>);
this.setState({count:x});
}
render() {
return (
<div key={"method"}>
<div>
{theSteps}
</div>
<button onClick={()=>this.AddMethod()}>Add another method?</button>
<button onClick={()=>this.getAllInfo()}>checking info</button>
</div>
);
}
}
export default MakeMethod;
And the Child component is the Steps:
import React from 'react';
import "./MakeRecipe.css"
const count=1;
class Steps extends React.Component {
constructor(props) {
super();
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.getState = this.getState.bind(this);
}
//function that returns the value in the text box
getState(){
debugger;
console.log(this.state.value); //tested and works
return this.state.value;
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<div>
<div id={this.props.count}>
<a>Step: {this.props.count}</a>
<input key={this.props.count} type="text" value={this.state.value} onChange={this.handleChange}/>
<br/>
</div>
<button onClick={()=>this.getState()}/>
</div>
);
}
}
export default Steps;

I think you don't exactly know the React lifecycle and how does really works passage from Parent to Child and vice versa.
This is a pseudo code that puts you in the right direction
import React from 'react';
import Steps from "./Steps";
const count=1;
const theSteps=[]; //list of step components i want to access
class MakeMethod extends React.Component {
constructor(props) {
super(props);
this.state = {
count : 1,
theSteps:[] ,
itemFromChild:''
}
this.AddMethod = this.AddMethod.bind(this);
this.getAllInfo=this.getAllInfo.bind(this);
this.state.theSteps.push(<Steps key={this.state.count} count={this.state.count} value={this.getAllInfo}/>);
}
//this is the function i want to access all my information from
getAllInfo=(itemFromChild)=> {
let method=[];
this.setState({ itemFromChild });
console.log(itemFromChild);
}
getAllInfoLocal() {
console.log(this.state.itemFromChild);
}
AddMethod(){
let x=this.state.count+1;
theSteps.push(<Steps key={x} count={x}/>);
this.setState({count:x});
}
render() {
return (
<div key={"method"}>
{this.state.itemFromChild}
<div>
{this.state.theSteps}
</div>
<button onClick={()=>this.AddMethod()}>Add another method?</button>
<button onClick={()=>this.getAllInfoLocal()}>checking info</button>
</div>
);
}
}
export default MakeMethod;
the other class...:
class Steps extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.getState = this.getState.bind(this);
}
//function that returns the value in the text box
getState(){
debugger;
console.log(this.state.value); //tested and works
return this.state.value;
}
handleChange = (event) =>{
console.log(event.target.value)
this.setState({value: event.target.value});
this.props.value(event.target.value);
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<div>
<div id={this.props.count}>
<a>Step: {this.props.count}</a>
<input key={this.props.count} type="text" value={this.state.value} onChange={this.handleChange}/>
<br/>
</div>
<button onClick={()=>this.getState()}/>
</div>
);
}
}
export default Steps;
Hope it helps...

Try to console log your component see what it will display.
Try i.getState. I think it should work since you're calling the property which is a function and not the callback of the function.
Also bind functions to the components like this:
getState = () => {
debugger;
console.log(this.state.value); //tested and works
return this.state.value;
}
since .bind has a lot of memory leaks.

Related

Controlled input in React

I am a newbie and recently learned about controlled input in React. Apparently one has to bind the function within the event handler that belongs to the component. Why?
class ControlledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
// binding in the constructor
this.handleChange = this.handleChange.bind(this)
}
// what the function does
handleChange(event){
this.setState({
input: event.target.value
})
}
render() {
return (
<div>
<input value = {this.state.input}
{//binding within the component}
<input onChange = {this.handleChange.bind(this)}>
</input>
{//why}
<h4>Controlled Input:</h4>
<p>{this.state.input}</p>
</div>
);
}
};
Sorry for bothering you

ReactJS - TypeError: Cannot set property 'propOne' of undefined

I have a simple React component:
import React from "react";
export default class Car extends React.Component {
render() {
return <h2>I am a {this.props.name}</h2>;
}
}
I am then importing this component into another component. This new component has a textbox which onChange event, tries to set a property which is being passed as a property to component one.
import React from "react";
import Car from "./Car";
class Garage extends React.Component {
constructor(props) {
super(props);
this.propOne = "Ford";
this.state = { value: "initial Value" };
}
handleChange(event) {
this.propOne = event.target.value;
}
render() {
return (
<>
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
<Car name={this.propOne} />
</>
);
}
}
export default Garage;
After running this code, I am getting TypeError: Cannot set property 'propOne' of undefined error
It's because you are making a new function and creating a new instance of this. Change it to an arrow function -
handleChange = (event) => {
this.propOne = event.target.value;
}
Or you can bind the this of handleChange to parent's this in the constructor of class
constructor(props) {
super(props);
this.propOne = "Ford";
this.state = { value: "initial Value" };
this.handleChange = this.handleChange.bind(this);
}
Kudos to #Atin for mentioning changing your handleChange function to an arrow function or binding this (I'll use the arrow function in my answer).
You'll also need to set propOne in state, and use this.setState(...) to update the value of propOne so that your component re-renders when propOne changes. When you just change this.propOne, React doesn't know that your component needs to re-render with the updated value, which is why you don't see the effect in Car. -> that's what the state is for. Whenever state is changed (with this.setState), React knows it needs to re-render that component, and it will pass your updated state property down to child components.
Try this for your state:
this.state = { value: "initial Value", propOne: "Ford" };
And this for your handleChange function:
handleChange = (event) => {
this.setState({ propOne: event.target.value });
}
Edit:
Additionally, your code (and the above, fixed code) is setting a property propOne on the state, but you're using this.state.value to update your input text value. Is there any reason you're trying to use two different properties - value and propOne? It seems they're being used for the same purpose, and therefore you can replace propOne with value everywhere like this:
constructor(props) {
super(props);
this.state = { value: "initial Value" };
}
handleChange = (event) => {
this.setState({ value: event.target.value });
}
render() {
return (
<>
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
<Car name={this.state.value} />
</>
);
}
Referenced error is thrown because you didn't bind this.
Other than that, you have a bunch of errors in your code, so I tried to address them all at once. If you have further questions, feel free to ask in the comments:
const { render } = ReactDOM
class Car extends React.Component {
render() {
return <h2>I am a {this.props.name}</h2>
}
}
class Garage extends React.Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
this.state = { value: "initial Value", propOne: 'Ford' }
}
handleChange(val) {
this.setState({value: val, propOne: val})
}
render() {
return (
<div>
<input
type="text"
value={this.state.value}
onChange={e => this.handleChange(e.target.value)}
/>
<Car name={this.state.propOne} />
</div>
)
}
}
render(
<Garage />,
document.getElementById('root')
)
<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><div id="root"></div>
p.s. and I agree with above answers, function components and hooks could make your app look much nicer

Refresh value react not value after action

I'm in the React work by 2 hours and I have a problem with how the writer separate correctly the component, an example I have these windows
When I click the button "Set" I change the value this.state.nameFramework, If I write all code inside the App component my helloApp work but if I write the code in the separate component it not work in the instant time but for change the value of the variable this.state.nameframework I had reloaded the page.
My code
require('normalize.css/normalize.css');
require('styles/App.css');
import React from 'react';
import InputFramework from 'components/InputFramework';
import ListPerson from 'components/ListPerson';
const list = [
{
objectID: 1,
'name': 'Vincenzo',
'surname': 'Palazzo'
},
{
objectID: 2,
'name': 'Sara',
'surname': 'Durante'
}
];
let name = 'Vincent';
let nameFramework = 'React';
class AppComponent extends React.Component {
constructor(props){
super(props);
this.state = {
list,
name,
nameFramework
};
this.onSelectPerson = this.onSelectPerson.bind(this);
this.onSubmitText = this.onSubmitText.bind(this);
this.onChangeNameFramework = this.onChangeNameFramework.bind(this);
}
onSubmitText(){
this.setState({nameFramework: this.state.nameFramework});
}
onChangeNameFramework(name){
this.state.nameFramework = name;
}
onSelectPerson(name) {
this.setState({name: name});
}
render() {
//This is no good for my programmer style, resolve it please
return (
<div className="index">
<InputFramework
name={this.state.name}
nameFramework={this.state.nameFramework}
onChange={this.onChangeNameFramework}
onClick={this.onSubmitText}
/>
<ListPerson
onClick={this.onSelectPerson}
list={this.state.list}/>
</div>
);
}
}
AppComponent.defaultProps = {
};
export default AppComponent;
Input component
require('normalize.css/normalize.css');
require('styles/App.css');
import React from 'react';
class InputFramework extends React.Component {
constructor(props){
super(props);
}
render() {
//This is no good for my programmer style, resolve it please
//The nameFramework not update
let {onChange, onClick, name} = this.props;
return (
<div className='index'>
<h1>Hello my name is {name} and I'm learning {this.props.nameFramework}</h1>
<from>
<input type='text'
onChange={event => onChange(event.target.value)}/>
<button type='submit' onClick={() => onClick}>Set</button>
</from>
</div>
);
}
}
InputFramework.defaultProps = {};
export default InputFramework;
List component
require('normalize.css/normalize.css');
require('styles/App.css');
import React from 'react';
class ListPerson extends React.Component {
constructor(props){
super(props);
}
render() {
//This is no good for my programmer style, resolve it please
const {onClick, list} = this.props;
return (
<div className="index">
<ul>
{list.map(function(item){
return (
<li key={item.objectID}>
{item.name}
<button type='button' onClick={() => onClick(item.name)}>Select</button>
</li>
)
})}
</ul>
</div>
);
}
}
ListPerson.defaultProps = {
};
export default ListPerson;
I this is a problem to how to write the code, now I ask you that you have more experience than me, can you help me to undestend.
You are trying to change (mutate) state directly in onChangeNameFramework handler.
Mutating state directly can lead to bugs.
State must be changed only by this.setState, so it must be like this:
onChangeNameFramework(name){
this.setState({
nameFramework: name
})
}
Here is the docs:
https://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly
Another problem is in InputFramework component, when you submit a form the page reloads, to prevent it, you should add e.preventDefault() like this:
class InputFramework extends React.Component {
render() {
//This is no good for my programmer style, resolve it please
//The nameFramework not update
let {onChange, onClick, name} = this.props;
const handleClick = (e) => {
e.preventDefault();
onClick();
}
return (
<div className='index'>
<h1>Hello my name is {name} and I'm learning {this.props.nameFramework}</h1>
<form>
<input type='text'
onChange={event => onChange(event.target.value)}/>
<button type='submit' onClick={handleClick}>Set</button>
</form>
</div>
);
}
}
Lastly in AppComponent the following code is redundant, since you are setting the same state:
onSubmitText(){
this.setState({nameFramework: this.state.nameFramework});
}
You already handle the change of framework name in onChangeNameFramework handler.
I think using both onSubmitText and onChangeNameFramework handler seems unnecesary here, only one of them will be enough.
Playground:
https://codesandbox.io/s/blue-frost-qutb0

get the value from a text input after it is updated in react function

I'm learning React and I need help understanding how to create functions for values that are updated asynchronously in the DOM. For instance, I have a text input within a component called header that looks like this:
export default class Header extends React.Component {
constructor(props){
super(props);
}
render(){
return (
<div className="Header">
<div><input onKeyDown={this.props.onEnter} id="filter-results" className="full" type="text" placeholder="search kks"></input></div>
<div><button className="full">SEARCH</button></div>
</div>
);
}
}
, which is used to filter search results. The onEnter function tries to use the value updated in the input:
class App extends React.Component {
constructor(props){
super(props);
this.state = {
categories: [],
searchResults: [],
};
this.filterSearch = this.filterSearch.bind(this);
}
filterSearch(){
var el = document.getElementById('filter-results').value
console.log(el)
var result = this.state.categories.filter(row => {
var rx = new RegExp(el)
return rx.test(row['id'])
});
console.log(result)
}
render(){
return (
<div className="App">
<Header onEnter={this.filterSearch}/>
</div>
);
}
}
When I type something into the input, the element's value is logged to the console. The problem is, what is logged is always one character less than what I expect to see. If I type 'a', I get '', 'ab' => 'a', etc. I can understand conceptually that when the function is triggered and the logging occurs the value hasn't yet been updated, but I don't know how to wait for the value to be updated and then work with it. Can anyone help me?
Use onChange instead.
//change handler
handler(e) {
console.log(e.target.value)
}
//input's onChange event
onChange={ this.handler.bind(this) }
1) You should not be using native javascript to get value by id. This is not react way of doing.
App.js
import React from "react";
import ReactDOM from "react-dom";
import Header from "./Header";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
categories: [],
searchResults: []
};
this.filterSearch = this.filterSearch.bind(this);
}
filterSearch(value) {
console.log(value);
var result = this.state.categories.filter(row => {
var rx = new RegExp(value);
return rx.test(row["id"]);
});
console.log(result);
}
render() {
return (
<div className="App">
<Header onEnter={this.filterSearch} />
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
// Header.js
import React from "react";
export default class Header extends React.Component {
constructor(props) {
super(props);
}
handleChange = ({ target }) => {
this.setState({
[target.name]: target.value
});
this.props.onEnter(target.value);
};
render() {
return (
<div className="Header">
<div>
<input
onChange={this.handleChange}
name="filter-results"
className="full"
type="text"
placeholder="search kks"
/>
</div>
<div>
<button className="full">SEARCH</button>
</div>
</div>
);
}
}

How to change props of a child?

I am new to React. This is probably a noob question.
I want to change the "filteredFields" prop of my MeteorGriddle component when the user clicks a checkbox. My JSX:
const bookingsPage = () => {
let filteredFields = ['userName'];
const handleClick = (e) => {
if (e.target.checked) {
filteredFields = ['userEmail'];
// How to propagate this change to the MeteorGriddle?
}
}
return (
<div>
<label><input type="checkbox" defaultChecked="true" value="userEmail" onClick={handleClick}/>Email</label>
<MeteorGriddle filteredFields={filteredFields}/>
</div>
);
};
I see two ways of solving your problem.
The first and easy way to do this:
Turn your bookingsPage component into statefull component instead of functional,
then you'd be able to create state inside it, and then change the state on event alongside with passing it to MeteorGriddle component.
So the code would be:
class bookingsPage extends React.Component {
getInitialState = () => {
filteredFields: []
}
handleClick = (e) => {
if (e.target.checked) {
const newFilteredFields =
[ ...this.state.filteredFields ].push('userEmail');
this.setState({ filteredFields: newFilteredFields });
}
}
render() {
return (
<div>
<label>
<input
type="checkbox"
defaultChecked="true"
value="userEmail"
onClick={this.handleClick}
/>
Email
</label>
<MeteorGriddle
filteredFields={this.state.filteredFields}
/>
</div>
);
}
};
Second and harder way to do this:
Take a look on Redux. It solves a problem of data flow in React.
The basic concept is that when you check you checkbox, you dispatch an action into reducer (aka your global data storage for react components), and then GriddleComponent recieves new state of your application with fresh data inside which tells him the checkbox is checked.
Say if you want me to write an example based on yours for you.
As #Maxx says, you should use a component with state. Then when you call the setState method, it will render again, updating the props of the children.
In your case this should work (also using ES6 notation):
import React from 'react';
import MeteorGriddle from '...whatever path...';
class bookingsPage extends React.Component {
state = {
filteredFields: ['userName']
};
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
if (e.target.checked) {
this.setState({
...this.state, //This is just a good practice to not overwrite other properties
filteredFields: ['userEmail']
});
}
}
render() {
const { filteredFields } = this.state;
return(
<div>
<label>
<input type="checkbox" defaultChecked="true" value="userEmail"
onChange={this.handleChange}/>Email
</label>
<MeteorGriddle filteredFields={filteredFields}/>
</div>
);
}
}
There are number of ways to achieve this, you can just try like the below code,
import React from 'react';
class bookingsPage extends React.Component {
state = {
filteredFields: ['userName']
};
constructor(props) {
super(props);
}
handleChange(e) {
if (e.target.checked) {
this.setState({
filteredFields: ['userEmail']
});
}
}
render() {
return(
<div>
<label>
<input type="checkbox" defaultChecked="true" value="userEmail"
onChange={this.handleChange.bind(this)}/>Email
</label>
<MeteorGriddle filteredFields={this.state.filteredFields}/>
</div>
);
}}

Categories