passing state value to a child component via props - javascript

i'm trying to pass the value entered by the user from the app component to the passTicket component. I tried invoking props to pass this state data but I keep getting an undefined error when attempting to access it. I'm new to react and it would be great if someone can help me make sense of what i'm getting wrong. This is a sample of what i'm trying to achieve.
This is my main component:
class App extends Component {
constructor(props){
super(props);
this.state = {
ticket:"",
};
this.changeTicket = this.changeTicket.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.keyPress = this.keyPress.bind(this);
}
changeTicket(e){
this.setState({
ticket : e.target.value,
})
}
handleSubmit(){
this.setState({
updatedTicket: this.state.ticket
});
}
keyPress(e){
if (e.keyCode ===13){
this.handleSubmit();
}
}
render() {
return (
<div className="App">
<header className="App-header">
<input type="text" placeholder="ENTER TICKET NUMBER" value={this.state.ticket} onKeyDown={this.keyPress} onChange={this.changeTicket}/>
</header>
</div>
);
}
}
and i'd like to be able to store the updatedTicket value in a variable which I can use in my PassTicket component. this is what i've attempted so far but the error it occurs is the following Uncaught TypeError: Cannot read property 'updatedTicket' of undefined
this is what my second component looks like:
class PassTicket extends Component {
transferredTicket(){
const myTicket = this.props.state.updatedTicket;
return myTicket
}
render() {
return (
<p>{this.transferredTicket()}</p>
);
}
}

When passing down a property from a parent to a child component, the property will be stored onto the props by the name it's passed through. For example:
class Parent extends Component {
state = {
ticket: '',
}
render() {
return <ChildComponent updatedTicket={this.state.ticket} />
}
}
class ChildComponent extends Component {
static propTypes = {
updatedTicket: PropTypes.string,
}
static defaultProps = {
updatedTicket: '',
}
render() {
return (
<div>{this.props.updatedTicket}</div>
);
}
}
In the example you've given, it doesn't seem like you're passing the state down to the component you're trying to access it in. In addition, it seems like you're trying to access the updatedTicket as a property of a state object, so just beware of how you're accessing your props.
Therefore, in order to access the updatedTicket property on the child component, you'll first need to import the PassTicket component, instantiate it in the parent (App) component, and pass the property down:
<PassTicket updateTicket={this.state.ticket} />
You would then be able to access the string in the PassTicket component like so - this.props.updateTicket

So .state in react is a local state that is only visible to the individual component. You can read more about it here: https://reactjs.org/docs/state-and-lifecycle.html
In order to pass your state around, you need to use the props system. So where you instantiate your component, you can pass in the state of the parent. For example:
<PassTicket ticket={this.state.updatedTicket}/>
Then inside your PassTicket render function, you can access the ticket prop:
render() {
const { ticket } = this.props
return (
<div>{ticket}</div>
)
}

Related

Why doesn't my React child component get value (re-render) when I change the parent state?

Background
I wrote an exact, short yet complete example of a Parent component with a nested Child component which simply attempts:
Alter a string in the Parent's state
See the Child component updated when the Parent's state value is altered (this.state.name)
Here's What It Looks Like
When the app loads a default value is passed from Parent state to child props.
Change The Name
All I want to do is allow the change of the name after the user adds a new name in the Parent's <input> and clicks the Parent's <button>
However, as you can see, when the user clicks the button only the Parent is rendered again.
Questions
Is it possible to get the Child to render the new value?
What am i doing wrong in this example -- why isn't it updating or
rendering the new value?
All Source Code
Here is all of the source code and you can view it and try it in my StackBlitz project.
I've kept it as simple as possible.
Parent component (DataLoader)
import * as React from 'react';
import { useState } from 'react';
import { Grid } from './Grid.tsx';
interface LoaderProps {
name: string;
}
export class DataLoader extends React.Component<LoaderProps, {}> {
state: any = {};
constructor(props: LoaderProps) {
super(props);
this.state.name = this.props.name;
this.changeName = this.changeName.bind(this);
}
render() {
const { name } = this.state;
let parentOutput = <span>{name}</span>;
return (
<div>
<button onClick={this.changeName}>Change Name</button>
<input id="mapvalue" type="text" placeholder="name" />
<hr id="parent" />
<div>### Parent ###</div>
<strong>Name</strong>: {parentOutput}
<hr id="child" />
<Grid childName={name} />
</div>
);
}
changeName() {
let newValue = document.querySelector('#mapvalue').value.toString();
console.log(newValue);
this.setState({
name: newValue,
});
}
}
Child component (Grid)
import * as React from 'react';
interface PropsParams {
childName: string;
}
export class Grid extends React.Component<PropsParams, {}> {
state: any = {};
constructor(props: PropsParams) {
super(props);
let counter = 0;
this.state = { childName: this.props.childName };
console.log(`CHILD -> this.state.name : ${this.state.childName}`);
}
render() {
const { childName } = this.state;
let mainChildOutput = <span>{childName}</span>;
return (
<div>
<div>### Child ####</div>
<strong>Name</strong>: {mainChildOutput}
</div>
);
}
}
App.tsx is set up like the following -- this is where default value comes in on props
import * as React from 'react';
import { DataLoader } from './DataLoader.tsx';
import './style.css';
export default function App() {
return (
<div>
<DataLoader name={'default value'} />
</div>
);
}
You're seeing two different values because you're tracking two different states. One in the parent component and one in the child component.
Don't duplicate data.
If the child component should always display the prop that's passed to it then don't track state in the child component, just display the prop that's passed to it. For example:
export class Grid extends React.Component<PropsParams, {}> {
render() {
const { childName } = this.props; // <--- read the value from props, not local state
let mainChildOutput = <span>{childName}</span>;
return (
<div>
<div>### Child ####</div>
<strong>Name</strong>: {mainChildOutput}
</div>
);
}
}
In the Child component, you set the prop childName value to state in the contructor ONLY. The constructor is executed ONLY WHEN THE COMPONENT IS MOUNTED. So, it doesn't know if the childName prop is changed later.
There are 2 solutions for this.
(1) Directly use this.props.childName without setting it to a state.
(2) Add a useEffect that updates the state value on prop change.
React.useEffect(() => {
this.state = {
childName: this.props.childName;
};
}, [this.props.childName]);
However, I recommend 1st solution since it's not a good practice to duplicate data.

How to pass data in two way data binding in class base component in ReactJS?

My problem that is in a child component, I have a state that updated via post method and I must show this state in my parent component both these component are the class base component
The ReactJS is a library with One directional data binding. So that is not possible to pass data like Angular or VueJS. you should pass a handler function to the child-component and then after the Axios answer update the local and also the parent component.
And there is a little hint here, there is no different for your situation between class components and functional components. pay attention to the sample code:
class ParentComponent extends React.Component {
state = {
data: undefined,
};
handleGetData = data => {
this.setState({
data,
});
};
render() {
return (
<ChildComponent onGetData={this.handleGetData} />
);
}
}
And now inside the ChildComponent you can access to the handler function:
class ChildComponent extends React.Component {
componentDidMound() {
const { onGetData } = this.props;
Axios
.get('someSampleUrl')
.then( (data) => {
onGetData(data); // running this update your parent state
});
}
render() {
return (
<div />
);
}
}
React's practice is one-directional. For best practice, you must 'Raise' the states into the parent component.
But if you were looking specifically to pass data up, you need to pass a callback down as a prop. use that callback function to pass the data up:
Parent:
<Parent>
<Child callback={console.log} />
</Parent>
Child:
const Child = (props) => {
const { callback } = props;
const postData = (...) => {
...
callback(result);
}
...
}

React props value is undefined

This is my parent code:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
tags: [],
};
}
componentDidMount() {
this.getTags();
}
getTags() {
//method gets tags from the backend
}
render() {
return <Child tags={this.state.tags} />;
}
}
And this is basically my child component:
export default class Child extends Component {
constructor(props) {
super(props);
this.state = {
tags: props.tags,
};
}
componentWillReceiveProps(nextProps) {
this.setState({
tags: nextProps.tags,
});
}
}
But when I console log tags somewhere in the Child component, it is undefined. Maybe it is undefined because the child component gets rendered before the parent component calls the method getTags? Or is there any other problem with this code? And how can I avoid this problem that tags are undefined in the child component?
Cheers
To avoid your problem, you shouldn't be rendering your Child component until the this.state.tags has any useful values.
Here is how you can do it and also show a "Loading..." text, so the user isn't worried the page is broken.
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
tags: [],
};
}
componentDidMount() {
this.getTags();
}
getTags() {
//method gets tags from the backend
}
render() {
return this.state.tags.length ? (
'Loading...'
) : (
<Child tags={this.state.tags} />
);
}
}
Your child component will definitely get rendered with the empty 'tags' array as a prop. Then, when getTags() returns the data, the newly populated tags array will be passed to the child as a prop, forcing the child to get re-rendered with the new data.
It should be the empty array though, not "undefined". You might check your getTags() method and the API you are calling to make sure you aren't getting "undefined" from there.
componentWillReceiveProps is legacy and should not be used. See the following link in the React docs for details: https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops
That documentation will walk you through what to do if you need to perform side effects as a result of changing props.
Right now the only thing is componentWillReceiveProps is to set local state to the props, which is totally superfluous. Is there something else you are needing to do there?

How to get React props outside component

I need a way to load the correct language of this script, and that info is a props value. Roughly put, it would look something like this:
class AddressInput extends React.Component {
render() {
return (
<PlacesAutocomplete
do={this.someStuff}
/>
);
}
}
export default scriptLoader(
`https://maps.googleapis.com/maps/api/js?&language=${this.props.language}`;
)(connect(mapStateToProps, mapDispatchToProps)(AddressInput));
I understand that this.props isn't accessible outside the component, so how would I be able to get scriptLoader to get a dynamic value?
You're basically there. You even have the idea of making it an HOC. Here is the impelementation:
function scriptLoader(WrappedComponent) {
return (props) => {
const dynamicUrl = `https://maps.googleapis.com/maps/api/js?&language=${props.language}`;
return <WrappedComponent {...props} url={dynamicUrl}/>
}
}
The HOC is accepting a prop called language, creates the url, and passes it down to the wrapped component, under the prop name url. Obviously, all these prop names can be changed at your discretion.
// usage in the parent
<AddressInput language="en"/>
// what is available in your presentational component
class AddressInput extends React.Component {
render() {
console.log(this.props.url) // https://maps.googleapis.com/maps/api/js?&language=us
return (
<PlacesAutocomplete
do={this.someStuff}
/>
);
}
}

Reactjs, parent component, state and props

I m actually learning reactjs and I m actually developping a little TODO list, wrapped inside of a "parent component" called TODO.
Inside of this parent, I want to get the current state of the TODO from the concerned store, and then pass this state to child component as property.
The problem is that I dont know where to initialize my parent state values.
In fact, I m using ES6 syntax, and so, I dont have getInitialState() function. It's written in the documentation that I should use component constructor to initialize these state values.
The fact is that if I want to initialize the state inside of my constructor, the this.context (Fluxible Context) is undefined actually.
I decided to move the initialization inside of componentDidMount, but it seems to be an anti pattern, and I need another solution. Can you help me ?
Here's my actual code :
import React from 'react';
import TodoTable from './TodoTable';
import ListStore from '../stores/ListStore';
class Todo extends React.Component {
constructor(props){
super(props);
this.state = {listItem:[]};
this._onStoreChange = this._onStoreChange.bind(this);
}
static contextTypes = {
executeAction: React.PropTypes.func.isRequired,
getStore: React.PropTypes.func.isRequired
};
componentDidMount() {
this.setState(this.getStoreState()); // this is what I need to move inside of the constructor
this.context.getStore(ListStore).addChangeListener(this._onStoreChange);
}
componentWillUnmount() {
this.context.getStore(ListStore).removeChangeListener(this._onStoreChange);
}
_onStoreChange () {
this.setState(this.getStoreState());
}
getStoreState() {
return {
listItem: this.context.getStore(ListStore).getItems() // gives undefined
}
}
add(e){
this.context.executeAction(function (actionContext, payload, done) {
actionContext.dispatch('ADD_ITEM', {name:'toto', key:new Date().getTime()});
});
}
render() {
return (
<div>
<button className='waves-effect waves-light btn' onClick={this.add.bind(this)}>Add</button>
<TodoTable listItems={this.state.listItem}></TodoTable>
</div>
);
}
}
export default Todo;
As a Fluxible user you should benefit from Fluxible addons:
connectToStores.
The following example will listen to changes in FooStore and BarStore and pass foo and bar as props to the Component when it is instantiated.
class Component extends React.Component {
render() {
return (
<ul>
<li>{this.props.foo}</li>
<li>{this.props.bar}</li>
</ul>
);
}
}
Component = connectToStores(Component, [FooStore, BarStore], (context, props) => ({
foo: context.getStore(FooStore).getFoo(),
bar: context.getStore(BarStore).getBar()
}));
export default Component;
Look into fluxible example for more details. Code exсerpt:
var connectToStores = require('fluxible-addons-react/connectToStores');
var TodoStore = require('../stores/TodoStore');
...
TodoApp = connectToStores(TodoApp, [TodoStore], function (context, props) {
return {
items: context.getStore(TodoStore).getAll()
};
});
As a result you wouldn't need to call setState, all store data will be in component's props.

Categories

Resources