ReactJS - Difference between generates a unique id via default props and state - reactjs

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.

Related

React state variable output as string but passes undefined as a prop

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>

Value not updating inside a ReactJS function

I have a ReactJs function that displays a simple dialogue box, and is intended to update a value for the parent component. The function looks like this:
export function MyDialogue(props: IMyProps) {
var myValue = 0;
return (
<div style={style}>
<label>Enter a number here: </label>
<input type="text" value={myValue} />
<button onClick={() => props.updateFunc(myValue)}>Update</button>
</div>
);
}
I've tried several variations of this; for example, passing in props.myValue, and even changing the function to a class and passing this.state.myValue in. In all but this example, myValue remains as it was in the parent class, and in this version, always 0.
updateFunc simply calls setState on the parent class, and having traced through it, it never gets called with the changed value.
I've seen some documentation that says to essentially handle the onChange event - is this the only way to make this work, or is there a way to implement data binding in React?
Just bind back your input to the parent's state via props.value;
MyDialogue.js
export function MyDialogue(props: IMyProps) {
return (
<div style={style}>
...
<input type="text" value={props.value} />
...
</div>
);
}
Parent.js
....
render(){
const { dialogueValue } = this.state;
return <MyDialuge value={dialogueValue} updateFunc={this.handleUpdate} />
}
You are using uncontrolled input because You are not keeping value of input inside state.
Solution is
With uncontrolled:
export class MyDialogue extends React.Component<IMyProps, {}>{
constructor() {
super();
this.input = React.createRef();
}
return (
<div style={style}>
<label>Enter a number here: </label>
<input type="text" ref={this.input} />
<button onClick={() => props.updateFunc(this.input.current.value)}>Update</button>
</div>
);
}
With controlled:
Maintain myValue state in parent and pass it to child.
and on change on input event call a function of parent which change myValue using setState,
Maintain myValye state inside MyDialogue and onClick pass it to parent.
You need to change this component to a stateful component
then do a two-way binding for your textbox and have it talk to local state and then use that state value to update parent component.
export class MyDialogue extends React.Component<IMyProps, {}>{
constructor() {
super();
this.state = {
myValue: 0
}
}
onChangeHandler = (event:any) =>{
this.setState({myValue:event.target.value});
}
return (
<div style={style}>
<label>Enter a number here: </label>
<input type="text" value={this.state.myValue} onChange={this.onChangeHandler}/>
<button onClick={() => props.updateFunc(this.state.myValue)}>Update</button>
</div>
);
}

React form input won't let me change value

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>
)
}
}

Use child component function to pass data to redux store via parent component/container

I'm new to React & Redux so it is difficult for me to explain in plain English but I'll try my best. I have a parent/container component that forms the main 'page' of my app. In this component I am rendering a header and various fields like so:
What I want to achieve is for any user input in the title field to be reflected where it currently says 'Untitled Practice' in the header.
The parent component looks like this (excluding various imports for brevity):
export class DrillCreator extends Component {
render() {
return (
<div>
<EditHeader />
<div className="container-fluid max-width-container">
<InputWithTooltip
type={'text'}
placeholderText={'Title'}
tooltipText={'Title Tooltip'}
required
/>
<InputWithTooltip
type={'textarea'}
placeholderText={'Summary'}
tooltipText={'Summary Tooltip'}
/>
<InputWithTooltip
type={'file'}
placeholderText={'Hero Image/Video'}
tooltipText={'Hero Image/Video Tooltip'}
/>
<InputWithTooltip
type={'select'}
options={['7', '8', '9', '10']}
placeholderText={'Ages'}
tooltipText={'Ages Tooltip'}
required
/>
</div>
</div>
);
}
}
The <InputWithTooltip /> component is essentially a container that renders the appropriate input along with a tooltip component:
export default class InputWithTooltip extends Component {
constructor(props) {
super(props);
this.state = {
textEntered: '',
};
}
render() {
let input = null;
if (this.props.type === 'text') {
input = (
<TextInput
placeholderText={this.props.placeholderText}
updateText={textEntered => this.setState({ textEntered })}
/>
);
} else if (this.props.type === 'select') {
input = (
<SelectInput
placeholderText={this.props.placeholderText}
updateText={textEntered => this.setState({ textEntered })}
/>
);
} else if (this.props.type === 'textarea') {
input = (
<TextAreaInput
placeholderText={this.props.placeholderText}
updateText={textEntered => this.setState({ textEntered })}
/>
);
} else if (this.props.type === 'file') {
input = (
<FileInput
placeholderText={this.props.placeholderText}
updateText={textEntered => this.setState({ textEntered })}
/>
);
}
return (
<div>
<InputTooltip tooltipText={this.props.tooltipText} />
{input}
</div>
);
}
}
As you can see, I have a textEntered state which is updated via an onChange function passed via the updateText props.
I have set up Redux so that I am able to call a dispatch function to set the title field in my reducer. This works fine if I simplify my parent component and simply call the <TextInput /> component which has the updateText prop:
export class DrillCreator extends Component {
constructor() {
super();
this.state = {
textEntered: '',
};
}
render() {
return (
<TextInput
placeholderText="Title"
updateText={(textEntered) => {
this.setState({ textEntered });
this.props.setDrillTitleAction({ textEntered });
}}
/>
);
}
}
const mapDispatchToProps = dispatch => ({
setDrillTitleAction: drillCreator => dispatch(setDrillTitle(drillCreator)),
});
export default connect(null, mapDispatchToProps)(DrillCreator);
The issue I have is that I want to call setDrillTitleAction from <InputWithTooltip /> instead of <TextInput /> as this is the only field in the form that I want to do anything special with.
Like I said I'm new to React and Redux so could be massively overcomplicating something or completely missing the point so any pointers would be massively helpful. Thanks.
You can pass the dispatch function from the container component in as a prop to the display component, and then call it when the value is changed.
In your case you need to pass setTitleDrillAction in as a prop to InputWithTooltip and then call if from within your updateText callbacks.
One thing to point out is that you will be storing the text box value multiple times - on the redux state and the InputWithTooltip state. You might choose to just make InputWithTooltip a stateless component which receives its value as a prop and dispatches updates to redux (via its parent container as described above).

Render map not displaying data on screen

I can't seem to get my renderJob mapping to render out properly. I have no errors in the console and can't figure out while nothing is showing up. Is there a problem with the way I'm mapping the object to the index?
import React, { Component } from 'react'
import PropTypes from 'prop-types'
const jbSampleData = [
{
name: 'A place',
location: 'USA',
engineer: "Contact Engineer",
service: "Last install"
},
]
class Job extends Component {
render() {
const renderJob = this.props.data.map((obj, idx) => {
return (
<div key={idx}>
<div>
<p>
<span>Name:</span> {obj.name} < br/>
<span>Location:</span> {obj.location} <br />
<span>Engineer Contact:</span> {obj.engineer} <br />
<span>Service:</span> {obj.service} <br />
</p>
</div>
</div>
);
});
return (
<div>
<renderJob />
Hello
</div>
)
}
}
Job.propTypes = {
data: PropTypes.arrayOf(
PropTypes.object
)
}
Job.defaultProps = {
data: jbSampleData
}
export default Job;
You are storing the map result basically an array in a variable renderJob, that is not a react component.
You need to render it like this:
return (
<div>
{renderJob}
Hello
</div>
)
Basically <renderJob> will get converted into:
React.createElement(renderJob, null); //new custom element
But in case of {renderJob} its value will get replaced, and all the ui items that variable is having will get rendered.
Why it is not throwing the error with <renderJob/> ?
Because name is stated with small letter so it will be considered as a built-in component like div etc, if you use <RenderJob/> it will throw error that RenderJob is not defined.
As per DOC:
When an element type starts with a lowercase letter, it refers to a
built-in component like <div> or <span> and results in a string 'div'
or 'span' passed to React.createElement. Types that start with a
capital letter like <Foo /> compile to React.createElement(Foo) and
correspond to a component defined or imported in your JavaScript file.
Instead of <renderJob /> change to {renderJob}

Resources