Props not updated - reactjs

I'm a new user of React and I try to dispatch a modification from my redux store into my components through a container component and props.
My problem is at the end, the data isn't updated. I tested and I figured out that in a Board component, I got the correct edited state (I edit a module's name in this.state.mlodules[1].name) but this value isn't sent in the Bloc component. Here is the render function of my Board component:
render() {
const modules = this.state.modules.map((module) => (
<Draggable key={module._id} x={module.position.x} y={module.position.y} inside={this.state.inside}>
<Bloc module={module} editModule={this.props.onModuleEdited(module._id)}/>
</Draggable>
));
return (
<div className="board"
onMouseLeave={this.mouseLeave}
onMouseEnter={this.mouseEnter}>
{modules}
</div>
);
}
And here is the render function of my Bloc component (I'm using a BlueprintJS editable text):
render() {
return (
<div className="pt-card pt-elevation-3 bloc">
<span onMouseDown={this.preventDrag}>
<EditableText
className="name"
defaultValue={this.props.module.name}
onChange={this.nameChanged}
/>
</span>
</div>
);
}
Any ideas ?
Thanks !

as i mentioned in my comment, you are assigning a defaultValue and not assigning a value prop.
according to their source code on line #77 you can see that there's a value prop.
Edit: As you can see in the docs, defaultValue is uncontrolled input where's value is a controlled input

I think, issue is defaultText. defaultText will assign the default text only on initial rendering it will not update the value. So instead of that assign the props value to value property.
Like this:
value = {this.props.module.name}
Note: But it will make the field read-only, if you don't update the props value (state of parent component) in onChange method.
Check this example, when you click on text 'Click Me' it will update the state value but text of input field will not change:
class App extends React.Component{
constructor(){
super();
this.state = {a:'hello'}
}
click(){
this.setState({a: 'world'},() => {
console.log('updated value: ', this.state.a)
})
}
render(){
return(
<div>
<Child value={this.state.a}/>
<p onClick={() => this.click()}>Click Me</p>
</div>
)
}
}
class Child extends React.Component{
render(){
console.log('this.props.value', this.props.value)
return(
<input defaultValue={this.props.value}/>
)
}
}
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'/>

Related

Issue refreshing child components in React after implementing React DnD

I'm trying to implement a sortable list of sortable lists in React, using React DnD. Prior to implementing the drag and drop side of things, all was working well.
I have a container component, which renders this:
<DndProvider backend={Backend}>
{this.state.classifications.map((classification, index) =>
<Classification id={classification.id} classification={classification} reportTemplate={this} key={classification.id} index={index} />
)}
</DndProvider>
The Classification extends Component, constructed like this:
constructor(props) {
super(props);
this.state = {
isEditing: false,
classification: props.classification
};
}
... and renders this (condensed for brevity):
<div className="panel-body">
<DndProvider backend={Backend}>
{this.state.classification.labels.map((label, index) =>
<Label id={label.id} label={label} reportTemplate={this.props.reportTemplate} key={label.id} index={index} />
)}
</DndProvider>
</div>
In turn, the Label also extends component, constructed like this:
constructor(props) {
super(props);
this.state = {
isEditing: false,
label: props.label
};
}
... and renders like this (again condensed for brevity):
return (
<div className={"panel panel-default panel-label " + (isDragging ? "dragging " : "") + (isOver ? " over" : "")}>
<div className="panel-heading" role="tab" id={"label-" + this.state.label.id}>
<div className="panel-title">
<div className="row">
<div className="col-xs-6 label-details">
{this.state.isEditing
? <input type="text" className="form-control" value={this.state.label.value} onChange={e => this.props.reportTemplate.onLabelValueChange(e, this.state.label.classificationId, this.state.label.id, 'value')} />
: <p className="form-control-static">{this.state.label.value}</p>
}
<div className="subuser-container">...</div>
</div>
</div>
</div>
</div>
);
All of this works well - when the user makes a change from the Label child component, it gets updated in the root component and everything syncs up and refreshes.
However, when implementing React DnD, both the Classification and Label components have been wrapped in Drag and Drop decorators, to provide sorting. The sorting via drag and drop works perfectly. However: this has caused the updating of elements to stop working (i.e., when a change is made from the Label, the update is fed through to the root Component correctly, but it doesn't then refresh down the tree).
Both the classification and label dnd implementations are like this in the render method:
return connectDropTarget(connectDragSource(...));
... and this when exporting the component:
export default DropTarget('classification', classificationTarget, classificationDropCollect)(DragSource('classification', classificationSource, classificationDragCollect)(Classification));
Interestingly, when a label is edited, the refresh does then occur when the user drags and drops the component. So its like the drag and drop will trigger a component refresh, but not the other onChange functions.
That was a long question, apologies. I'm almost certain someone else will have experienced this issue, so any pointers gratefully appreciated.
Ok so i've basically answered my own question, but many thanks to those who posted comments on here to help narrow it down.
The answer was that my state object is complex and deep, and the component/decorator implementation of React DnD seems to have an issue with that. My assumption is that there is some behaviour in the decorator's shouldComponentUpdate that is blocking the components refresh when a deep property is update. React DnD's own documentation refers to the decorators as "legacy", which is fair enough.
I updated our components to use hooks instead of decorators, and it all sprang to life. So the answer is this, if your DnD implementation is deep and complex, use hooks.
Here is an example of your code without DnD that won't work either because the prop is copied to state in the constructor and on concurrent renders the prop is never used again, only the state.
In Child you can see that it will render but counter never changes, in CorrectChild the counter will change because it's just using props.counter.
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {//copied only once in constructor
counterFromParent: props.counter,
};
}
rendered=0;
render() {
this.rendered++;
return (
<div>
<h3>in broken Child rendered {this.rendered} times</h3>
<button onClick={this.props.up}>UP</button>
<pre>
{JSON.stringify(this.state, undefined, 2)}
</pre>
</div>
);
}
}
class CorrectChild extends React.Component {
render() {
//not copying props.count, just using it
return (
<div>
<h3>in correct Child</h3>
<button onClick={this.props.up}>UP</button>
<pre>
{JSON.stringify(this.props, undefined, 2)}
</pre>
</div>
);
}
}
function App() {
const [state, setState] = React.useState({ counter: 1 });
const up = React.useCallback(
() =>
setState((state) => ({
...state,
counter: state.counter + 1,
})),
[]
);
return (
<div>
<h3>state in App:</h3>
<pre>{JSON.stringify(state, undefined, 2)}</pre>
<Child counter={state.counter} up={up} />
<CorrectChild counter={state.counter} up={up} />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
If you still experience that without DnD your code "works" then please provide a Minimal, Reproducible Example of it "not working".

office-ui-fabric-react / TextField input properties alternative to onChanged

I'm currently using the TextField from office UI fabric and using the onChanged property to assign my prop in react the value being entered similar to their GitHub example.
However, the event handler is called for each element being entered. How can I make a call to the event handler(this._onChange) only when the user finishes entering the entire text (eg on focus dismiss, etc)?
I'm guessing that would be more efficient than logging an event with each letter being entered.
New to react. Appreciate your help!
This is more of an in-general way React uses the input onChange event. With React, you want to keep the value of your input in state somewhere, whether that is component state or a global store like Redux. Your state and UI should always be in sync. Because of this, the onChange event fires for every character that is entered/removed so that you can update that state to reflect the new value. Inputs written this way are called Controlled Components and you can read more about them and see some examples at https://reactjs.org/docs/forms.html.
That being said, you can detect when the user leaves the input with the onBlur event, though I would not recommend using that to update the state with the value as you'll see that a Controlled Component will act read-only when you don't update the state in the onChange event. You will have to use an Uncontrolled Component, typically setting the initial value with defaultValue instead of value and making things more difficult for yourself.
// CORRECT IMPLEMENTATION
class ControlledForm extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
name: 'example'
};
this.handleNameChange = this.handleNameChange.bind(this);
}
handleNameChange(e) {
this.setState({
name: e.target.value
});
}
render() {
return (
<div>
<h1>Controlled Form</h1>
<input type="text" value={this.state.name} onChange={this.handleNameChange} />
<p>{this.state.name}</p>
</div>
);
}
}
// INPUT DOES NOT WORK
class BuggyUncontrolledForm extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
name: 'example'
};
}
render() {
return (
<div>
<h1>Buggy Uncontrolled Form</h1>
<input type="text" value={this.state.name} />
<p>{this.state.name}</p>
</div>
);
}
}
// NOT RECOMMENDED
class UncontrolledForm extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
name: 'example'
};
this.handleNameChange = this.handleNameChange.bind(this);
}
handleNameChange(e) {
this.setState({
name: e.target.value
});
}
render() {
return (
<div>
<h1>Uncontrolled Form</h1>
<input type="text" defaultValue={this.state.name} onBlur={this.handleNameChange} />
<p>{this.state.name}</p>
</div>
);
}
}
ReactDOM.render(
<div>
<ControlledForm />
<BuggyUncontrolledForm />
<UncontrolledForm />
</div>
, 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>
<div id="root"></div>
You may consider using React's onBlur prop which will be invoked when the input loses focus. Here is an example Codepen which window.alert's the <TextField> component's current value when it loses focus: https://codepen.io/kevintcoughlin/pen/zmdaJa?editors=1010.
Here is the code:
const {
Fabric,
TextField
} = window.Fabric;
class Content extends React.Component {
public render() {
return (
<Fabric>
<TextField onBlur={this.onBlur} />
</Fabric>
);
}
private onBlur(ev: React.FocusEvent<HTMLInputElement>) {
window.alert(ev.target.value);
}
}
ReactDOM.render(
<Content />,
document.getElementById('content')
);
I hope you find that helpful.
References
https://reactjs.org/docs/events.html#focus-events
You can keep your state and UI in sync but use things like your own deferred validation error-check functions to check if the value is good/bad AND/or if you want to do something like logging based on the value only after a certain amount of time passes. Some examples from this page copied below for quick reference - you can do whatever you want in your "_getErrorMessage" function (https://github.com/OfficeDev/office-ui-fabric-react/blob/master/packages/office-ui-fabric-react/src/components/TextField/examples/TextField.ErrorMessage.Example.tsx):
<TextField
label="Deferred string-based validation"
placeholder="Validates after user stops typing for 2 seconds"
onGetErrorMessage={this._getErrorMessage}
deferredValidationTime={2000}
/>
<TextField
label="Validates only on focus and blur"
placeholder="Validates only on input focus and blur"
onGetErrorMessage={this._getErrorMessage}
validateOnFocusIn
validateOnFocusOut
/>

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

i am not able retrieve array elements one at a time if i call them in my component all the elements are retrieved at a time

I want to load my array element when an event is occurred by referencing the key i tried different variables for the key but it would not accept all the elements of the array are being displayed if i give index as the key.
I am new to Reactjs and not very familiar with all the syntax and concept can somebody help me with the logic to solve this.
The event I am triggering is onClick or onChange.
`var Qstn =['A','B','C','D','E','F','G','H','I','J'];
<div>
{Qstn.map(function(Q,index){
return <span className="col-md-4 txt" key={index}>{Q}</span>
})}
</div>`
Ok I made a codepen with an example
http://codepen.io/lucaskatayama/pen/QGGwKR
It's using ES6 classes components, but it's easy to translate.
You need to set initial state to an empty array like [].
On click button, it call onClick() method which uses this.setState({}) to change component state.
When React notice state changes, it re-render the component.
class Hello extends React.Component {
constructor(){
super();
//Initial State
this.state = {
Qstn : []
}
}
onClick(){
//Called on click button
// Set state to whatever you want
this.setState({Qstn : ['A','B','C','D','E','F','G','H','I','J']})
}
render(){
let Qstn = this.state.Qstn; // load state and render
return (
<div>
<button onClick={() => this.onClick()}>Click</button>
<div>
{Qstn.map(function(Q,index){
return <span className="col-md-4 txt" key={index}>{Q}</span>
})}
</div>
</div>
)
}
}
ReactDOM.render(<Hello />, document.getElementById('container'))

How do I reset the defaultValue for a React input

I have a set of React input elements that have a defaultValue set. The values are updated with an onBlur event.
I also have another action on the page that updates all values in these input elements. Is there a way to force react to render the new defaulValues when this happens?
I can't easily use onChange since it would trigger a premature rerender (The inputs contain a display order value and a premature rerender would move them).
I could create a duplicate state, one for the real values that is only updated with onBlur and one to update the value in the input element while it is being edited. This would be far from ideal. It would be so much simpler to just reset the default values.
As mentioned in https://stackoverflow.com/a/21750576/275501, you can assign a key to the outer element of your rendered component, controlled by state. This means you have a "switch" to completely reset the component because React considers a new key to indicate an entirely new element.
e.g.
class MyComponent extends React.Component {
constructor() {
super();
this.state = {
key: Date.now(),
counter: 0
};
}
updateCounter() {
this.setState( { counter: this.state.counter + 1 } );
}
updateCounterAndReset() {
this.updateCounter();
this.setState( { key: Date.now() } );
}
render() { return (
<div key={this.state.key}>
<p>
Input with default value:
<input type="text" defaultValue={Date.now()} />
</p>
<p>
Counter: {this.state.counter}
<button onClick={this.updateCounter.bind( this )}>Update counter</button>
<button onClick={this.updateCounterAndReset.bind( this )}>Update counter AND reset component</button>
</p>
</div>
); }
}
ReactDOM.render( <MyComponent />, document.querySelector( "#container" ) );
<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="container" />
I've solved this by using both onBlur and onChange and only keeping track of the currently active input element in the state.
If there is a way to reset the module so that it re-displays the new default values then I'll mark that as correct.
state = {
inFocusIndex: null,
inFocusDisplayOrder: 0,
};
onOrderBlur() {
const productRow = this.props.products[this.state.inFocusIndex];
const oldDisplayORder = productRow.displayOrder;
// This can change all the display order values in the products array
this.props.updateDisplayOrder(
this.props.groupId,
productRow.productGroupLinkId,
oldDisplayORder,
this.state.inFocusDisplayOrder
);
this.setState({ inFocusIndex: null });
}
onOrderChanged(index, event) {
this.setState({
inFocusIndex: index,
inFocusDisplayOrder: event.target.value,
});
}
In the render function:
{this.props.products.map((row, index) => {
return (
<input
type="text"
value={index === this.state.inFocusIndex ? this.state.inFocusDisplayOrder : row.displayOrder}
className={styles.displayOrder}
onChange={this.onOrderChanged.bind(this, index)}
onBlur={this.onOrderBlur.bind(this)} />
);
})}

Resources