I cannot understand why I am able to render the value of a state variable without an issue but when I pass it as a prop to a child component the value becomes undefined.
truncated source code below:
this.state.pdf renders the url correctly using the code snippet below.
render() {
return (
....
<div>
<label>PDF:</label>
<textarea rows="4" cols="100" name="pdf" value={this.state.pdf} onChange={this.onChange} placeholder="PDF URL" />
</div>
....
...later on in the render function I pass the same state variable to another component to render the actual PDF.
<div>
<MammaPDF pdf={this.state.pdf} />
</div>
MammaPDF class snippet:
class MammaPDF extends Component {
constructor(props) {
super(props);
this.state = {
numPages: null,
pageNumber: 1,
pdf: null,
}
}
componentDidMount(){
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
console.log("MammaPDF PROPS: ",this.props); <--- pdf is undefined
const pdf = this.props.pdf; <---pdf is undefined
....
}
There could be a case, this.state.pdf was not resolved when it was passed to MammaPdf component. So you should wait for it to be resolved and then call the component.
Can you try below code:-
<div>
{this.state.pdf && <MammaPDF pdf={this.state.pdf} />}
</div>
Related
Setup: I have set up a two react components in a parent child relationship. The parent has a state that can be changed by press of a button on parent itself.
Expected behaviour: In the child, I have an input field and I want the state to change to the value I send in the input field on the press of the submit button. I have set up the parent and the child as follows:
What I have tried: I going through this answer and this youtube video but I guess I am not smart enough to make sense of it.
This is what my code looks like
Parent:
class App extends Component {
state = {
value:"someValue"
};
changeValue = (value) => {
this.setState({
value
})
}
render() {
return (
<div>
<p>this is the value from state: {this.state.value}</p>
<button onClick={()=>this.changeValue("valueFromParentComponent")}>Press to change value from parent component</button>
<br/><br/>
<Child getChildInputOnSubmit={()=>this.changeValue()} />
</div>
);
}
}
And this is what the child looks like
Child:
class Child extends Component {
state = {
}
sendChildData = (childInputValue) => {
console.group("This is the data from the input of the child component")
console.log("==============")
console.log(childInputValue)
console.log("==============")
}
render() {
return (
<div>
This is the child component
<br /><br />
<form>
<input type="text" placeholder="Some placeholder"></input>
<button onSubmit={this.sendChildData()} type="submit">Send child's input to parent</button>
</form>
</div>);
}
}
The React behaviour encourages to implement an inverse data flow inside a component hierarchy. Meaning that the child components can receive parent methods through props, this methods will work as callbacks, allowing to receive data, trigger behaviours, update his state and more.
I attach a StackBlitz example, showing how this concept would work in your setup https://stackblitz.com/edit/react-jsv5jo
Edit: Here a few extra tips applied on the example:
To work with inputs on React, a common setup consists on listen the onChange event to receive new data and update the component state. Then, this state is used in the value attribute to update the input content on DOM.
Listen the onSubmit event on the form tag instead on submit button, and remember to add some logic to avoid reloading.
Another good practice on React components is initialize your state object inside the Constructor (In case to be working with a Class Component) and write methods to avoid bloat the render one (Be sure to bind the extra methods on your constructor to avoid invocation problems)
Callbacks are used to pass data from Child component to Parent component in React.
We wright function in Parent component that will receive value and pass this function to child component through Props.
class Parent extends Component {
state = {
value: 'someValue'
};
changeValue = value => {
this.setState({
value
});
};
render() {
return (
<div>
<p>this is the value from state: {this.state.value}</p>
<button onClick={() => this.changeValue('valueFromParentComponent')}>
Press to change value from parent component
</button>
<br></br>
<Child getChildInputOnSubmit={this.changeValue} />
</div>
);
}
}
Now in Child component we call Parents function that we passed in props and send value.
class Child extends Component {
constructor(props) {
super(props);
this.state = {
Childvalue: ''
};
}
handleChange = event => {
event.preventDefault();
this.setState({ Childvalue: event.target.value });
};
sendToParent = () => {
//here calling Parents changeValue
this.props.getChildInputOnSubmit(this.state.Childvalue);
};
render() {
return (
<div>
This is the child Component
<br></br>
<form action='#' onSubmit={this.sendToParent}>
<input
type='text'
placeholder='Some placeholder'
value={this.state.Childvalue}
onChange={this.handleChange}
></input>
<button type='submit'>Send child's input to parent</button>
</form>
</div>
);
}
}
For some reason i need to generate some unique ID to use them as CSS ID Selectors in a ReactJs component , I've done two examples:
1) Defining a random value in default props, see Test with default props below
=>It doesn't work
2) Defining a random value in state see Test with state below
=>It works
Can some one explain why when i use default props i've got always the same value?
Codesanbox :
Codesanbox link with all code example
Source code :
function App() {
return (
<div className="App">
<h1>Components with random default props</h1>
<DymmyComponentProps />
<br />
<DymmyComponentProps />
<br />
<DymmyComponentProps />
<br />
<DymmyComponentProps />
<h1>Components with random state</h1>
<DymmyComponenState />
<br />
<DymmyComponenState />
<br />
<DymmyComponenState />
<br />
<DymmyComponenState />
</div>
);
}
Test with default props:
class DymmyComponentProps extends Component {
static defaultProps = {
id: `Unique id is: ${Math.random()
.toString(36)
.substring(7)}`
};
render() {
const { id } = this.props;
return <p id={id}>{id}</p>;
}
}
Test with state : (it work)
class DymmyComponenState extends Component {
state = {
id: `Unique id is: ${Math.random()
.toString(36)
.substring(7)}`
};
render() {
const { id } = this.state;
return <p id={id}>{id}</p>;
}
}
Can some one explain why when i use default props i've got always the same value?
As you can see, defaultProps is a static property
static defaultProps = {
...
};
Wich means it doesn't change on every new class, it's the same for every class.
And if you think about it, if the value is different on every instance of the class, it wouldn't be considered default, and in your case, it shouldn't be.
I have a component in a React class in my Laravel project which is a simple form with one input field. It houses a phone number which I have retrieved from the database and passed back through the reducer and into the component as a prop. Using this, I have passed it through to the module as a prop which then populates the field with the currently saved value:
<OutOfOfficeContactNumberForm
show={props.showOutOfOffice}
value={props.outOfOfficeNumber}
handleChange={console.log("changed")}
/>
I have a handleChange on here which is supposed to fire a console log, but it only ever displays on page load. Here is my form module class:
class OutOfOfficeContactNumberForm extends React.Component {
render() {
const { show, value, handleChange } = this.props;
if(!show) return null;
return (
<div>
<p>
Please supply an Out of Office contact number to continue.
</p>
<InputGroup layout="inline">
<Label layout="inline" required={true}>Out of Office Contact Number</Label>
<Input onChange={handleChange} value={value} layout="inline" id="out-of-office-number" name="out_of_office_contact_number" />
</InputGroup>
</div>
);
}
}
export default (CSSModules(OutOfOfficeContactNumberForm, style));
The form is embedded in my parent component, as follows:
return (
<SectionCategoriesSettingsForm
isSubmitting={this.state.isSubmitting}
page={this.props.page}
show={this.props.show}
categories={this.props.categories}
submitSectionCategoriesSettings={this._submit.bind(this, 'add')}
updateSelectedCategories={this._updateSelectedCategories.bind(this)}
selectedCategoryIds={this.state.selectedCategoryIds}
storedUserCategories={this.props.selectedCategories}
outOfOfficeNumber={this.state.outOfOfficeNumber}
onUpdateContactNumber={this._updateContactNumber.bind(this)}
/>
);
In my componentWillReceiveProps() function, I set the state as follows:
if (nextProps.selectedCategories && nextProps.selectedCategories.length > 0) {
this.setState({
outOfOfficeNumber: nextProps.outOfOfficeNumber,
selectedCategoryIds: nextProps.selectedCategories.map(c => c.id)
});
}
I'm pretty sure the reason it's not changing is because it's pre-loaded from the state which doesn't change - but if I cannot edit the field how can I get it to register a change?
EDIT: Just to clarify there are also checkboxes in this form for the user to change their preferences, and the data retrieved for them is set the same way but I am able to check and uncheck those no problem
Changes:
1- onChange expect a function and you are assigning a value that's why, put the console statement inside a function and pass that function toOutOfOfficeContactNumberForm component , like this:
handleChange={() => console.log("changed")}
2- You are using controlled component (using the value property), so you need to update the value inside onChange function otherwise it will not allow you to change means input values will not be not reflect in ui.
Check example:
class App extends React.Component {
state = {
input1: '',
input2: '',
}
onChange = (e) => this.setState({ input2: e.target.value })
render() {
return(
<div>
Without updating value inside onChange
<input value={this.state.input1} onChange={console.log('value')} />
<br />
Updating value in onChange
<input value={this.state.input2} onChange={this.onChange} />
</div>
)
}
}
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' />
I think the best way is when you get data from database put it to state and pass the state to input and remember if you want to see input changes in typing, use a function to handle the change and that function should change state value.
class payloadcontainer extends Component {
constructor(props) {
super(props)
this.state = {
number:1
}
}
render() {
return (
<div>
<input value={this.state.number} onChange={(e)=>this.setState({number:e.target.value})}></input>
<button onClick={()=>this.props.buyCake(this.state.number)}><h3>buy {this.state.number} cake </h3></button>
</div>
)
}
}
Im learning react and very new to this, I am tinkering with something to understand this more.
I would like to know if it is possible to console.log the state of the CHILD using a prop passed down by the parent.
Example :
Child component ( has its own state)
Parentcomponent ( has its own state)
Child Component
this.state={
animal:'Lion'
}
<button onClick{this.props.giveMeState}>
And that, I would want to console the state ( animal:Lion)
Parent Component
this.state={
name: 'John'
}
giveMeState(){ ? what can go here, or is it not that simple ?
)
}
Codepen of example
Parent component cannot query the state of the child component. At least, that's not the intended design of React.
What I think you're asking is how to coordinate the state of child with parent, and you're on the right track to use a prop to pass the state from child to parent.
Perhaps a complete example that does what you want would look like this:
class Parent extends React.Component {
state = { name: "John" }
handleChildAnimal = animal =>
this.setState({ animal });
handleClick = e =>
console.log(`Child animal: ${this.state.animal}`);
render() {
return (
<div>
<Child onAnimal={this.handleChildAnimal} />
<button onClick={this.handleClick}>Tell me Animal state</button>
</div>
);
}
}
class Child extends React.Component {
state = { animal: "Lion" }
handleClick = e => {
console.log(`Animal: ${this.state.animal}`);
this.props.onAnimal(this.state.animal);
}
render() {
return (
<button onClick={this.handleClick}>{this.state.animal}</button>
);
}
}
Demo on CodePen.io
if you want to pass the state value of the child to the parent,
you can do it like this,
In the child component add another function getState and call the reference function giveMeState through this function
...
constructor(props) {
super(props)
this.state={ animal:'Lion' }
this.getState = this.getState.bind(this)
}
getState(){
this.props.giveMeState(this.state)
}
....
<button onClick={this.getState}>
....
and also redefine the parent function so that it takes a parameter
and the console.log that parameter
Not sure if this is a good pattern though
Here is another answer just for giving another example.
It does not fulfill your question and as told fulfilling your question would not be the best approach. Maybe you should try to think differently while working React and states.
App
class App extends React.Component {
state = {
input: "initial input state",
childState: "right now I don't know child state",
};
handleInputChange = e => this.setState({ input: e.target.value });
handleChildState = ( childState ) => this.setState( { childState } )
render() {
return (
<div style={styles}>
<h4>This is parent component.</h4>
<p>Input state is: {this.state.input} </p>
<p>Child state is: {this.state.childState}</p>
<hr />
<Input
onInputChange={this.handleInputChange}
getState={this.handleChildState}
/>
</div>
);
}
}
Child component as Input
class Input extends React.Component {
state = {
myState: "some state"
};
handleSendState = () => this.props.getState(this.state.myState);
handleState = e => this.setState({ myState: e.target.value });
render() {
return (
<div>
<h4>This is Child coponent</h4>
<button onClick={this.handleSendState}>
Click me to get child state
</button>
<p>This is my state: {this.state.myState}</p>
<p>Write something to change child's state.</p>
<input type="text" onChange={this.handleState} />
<p>
Write something to change parent's input state
</p>
<input type="text" onChange={this.props.onInputChange} />
</div>
);
}
}
I'm trying to wrap bootstrap into components with integrated form validation.
short:
Let's say I have
<Form>
<FieldGroup>
<Field rules={'required'}/>
</FieldGroup>
</Form>
Once Field pases validation, how can I notify FieldGroup (parent node) to add a class?
I created a simplified codepen version here
I would like depending on validation status, then change the state of FieldGroup So I can properly change the class name. (add has-warning has-danger etc) and ultimately add class to the Form component.
You need to pass a callback to the child component. I just forked your codepen and added some snippet as below.
http://codepen.io/andretw/pen/xRENee
Here is the main concept,
Make a callback function in "parent" component and pass it to the "child" component
i.e. The child component needs an extra prop to get the callback:
<Form>
<FieldGroup>
<Field rules={'required'} cb={yourCallbackFunc}/>
</FieldGroup>
</Form>
In <FieldGroup /> (parent):
class FieldGroup extends React.Component{
constructor(props){
super(props);
this.state = {
color: 'blue'
}
}
cb (msg) {
console.log('doing things here', msg)
}
render() {
const childrenWithProps = React.Children.map(this.props.children,
child => React.cloneElement(child, {
cb: this.cb
})
)
return (
<div class='fields-group'>
<label> field </label>
{ childrenWithProps }
</div>
);
}
};
In <Field /> (child):
class Field extends React.Component{
constructor(props){
super(props);
this.state = {
empty: true
}
this.validate = this.validate.bind(this);
}
validate(e){
let val = e.target.value;
console.log(!val);
this.setState({empty: !val});
//here to notify parent to add a color style!
// do call back here or you may no need to return.
this.props.cb(val)
return !val;
}
render() {
return (
<div>
<input type='text' onBlur ={(event) => this.validate(event)}/>
{this.state.empty && 'empty'}
</div>
);
}
};
And you can do the things you want in the callback function. (You can also pass a callback from <Form /> to the grandson and get it work, but you need to rethink the design of it is good or not.)