I have a simple react component which takes in a prop which is an Object.
I am trying to pass this in to a dispatch call to get it added to a list.
This works fine if I pass in a static value for the description field.
But fails when I use the dynamic field. I have checked via console log and the dynamic field definitely has a value and it is of type String as expected.
Why does it work when I use a static value (done solely for testing) for now but fails when I use the dynamic value?
Don't see any issues with my store, action, reducer setup cos it updates state fine with a static value.
Please advice. Thank you.
Experiencing this issue inside the ExpenseForm component existing within AddExpensePage where I
am using the expense value being passed in to perform the dispatch.
This is the working example with the static String value.
import React from "react";
import ExpenseForm from "./ExpenseForm";
import {connect} from "react-redux";
const AddExpensePage = (props) =>
<div>
<h1>Add Expense</h1>
<ExpenseForm onSubmit={(expense) => {
// This is what I want to do but unable to cos of the issue mentioned with description value thus breaking it out as follows.
// props.dispatch(addExpense(expense))
console.log(expense.description) // I do get a value like 'sample input' thus no issue with value
console.log(typeof expense.description) // it is of type String as expected
// breaking out the expense to be able to hard code value for description.
const tempFakeExpense = {
description: 'HARD_CODED_STRING_WHICH_WORKS', // expense.description will not update
note: expense.note,
amount: expense.amount,
createdAt: expense.createdAt
}
props.dispatch(addExpense(tempFakeExpense))
props.history.push('/');
}}/>
</div>
export default connect()(AddExpensePage);
The above doesn't work if I use the dynamic value as follows.
Meaning, if list had 10 items, it remains as 10. It doesn't append list to make it 11.
const actualExpense = {
description: expense.description, // not using hard coded value, thus not updating. Why?
note: expense.note,
amount: expense.amount,
createdAt: expense.createdAt
}
Expecting the issue to exist in above code. Adding the Expense component code below for reference in case there is some form of red herring.
There is no Redux in this component cos the state values are only for input tracking and not used anywhere else. Thus sticking to plain state/ setstate style of state management.
Note: The value got passed correctly to above component thus this state management works fine for ExpenseForm component and it doesn't need Redux to work in this component.
import React from 'react';
import moment from "moment";
import {SingleDatePicker} from "react-dates";
import 'react-dates/lib/css/_datepicker.css';
class ExpenseForm extends React.Component {
state = {
description: '',
note: '',
amount: '',
createdAt: moment(),
focused: false
}
render() {
return (
<div>
<form onSubmit={(e) => {
e.preventDefault();
if(this.state.description && this.state.amount) {
this.props.onSubmit({
description: this.state.description,
amount: parseFloat(this.state.amount),
note: this.state.note,
createdAt: this.state.createdAt.valueOf()
})
}
}}>
<input
type='text'
placeholder='description...'
autoFocus
value={this.state.description}
onChange={e => {
this.setState(() => ({
description: e.target.value
}))
}}
/>
{/* Setups for other fields */}
<button>Add Expense</button>
</form>
</div>
);
}
}
export default ExpenseForm;
Related
I am trying to make a TO DO list app using Reactjs. onChangeTitleHandler is not setting the state of title when I type the title and the same happen with the onChangeTaskHandler. the new state is not setting up.
This is the Cockpit component.
const cockpit = (props) => {
return (
<div>
<form onSubmit={props.submitted}>
<input type="text" placeholder="Title" onChange={props.ChangeTitle} />
<textarea rows={2} onChange={props.changeTask} />
<input type='submit' value='ADD TASK'/>
</form>
</div>
);
}
This is the code that I've tried.
this is my App.js file
import "./App.css";
import { React, Component } from "react";
import Cockpit from "./Components/Cockpit";
class App extends Component {
state = {
title:'',
task: '',
allTask : []
};
onChangeTitleHandler = (event)=>{
this.setState={
title: event.target.value,
}
console.log(this.state.title);
}
onChangeTaskHandler =(event)=>{
this.setState={
task: event.target.value,
}
console.log(this.state.title);
}
onSubmitHandler =(event) => {
const tasks = this.state.allTask;
tasks.push({
title:this.state.title,
task:this.state.task
})
this.setState={
allTask:tasks
}
console.log(tasks);
event.preventDefault();
};
render() {
return (
<div className="App">
<h1 className="heading">Prioritise Your Tasks</h1>
<Cockpit
ChangeTitle = {this.onChangeTitleHandler}
changeTask={this.onChangeTaskHandler}
submitted = {this.onSubmitHandler}
/>
</div>
);
}
}
export default App;
I want that onSubmit the new state of title and task added in to the allTask array.
There is a lot going on confusingly or wrongly in your example.
The main thing is that pointed out by Nils: that you are assigning a value to the setState function rather than using it as intended (calling it).
So instead of
onChangeTaskHandler =(event)=>{
this.setState={
task: event.target.value,
}
console.log(this.state.title);
}
you need to do
onChangeTaskHandler = event => {
this.setState({ task: event.target.value })
}
Which leads me to one other thing that may be confusing your "output", which is that your are logging this.state.title immediately after calling the this.setState function. This won't have your expected outcome as the setState function is asynchronous, and therefore may not be as fast as you need it to for it to show the updated state in the console.log call.
As a latter point, from my personal point of view, your code style is somewhat confusing in the way you are naming things. Calling "submitted" to the onSubmit function handler sounds more of a boolean than of a function. Having properties starting with uppercase letters like ChangeTitle may be commonly interpreted as that being a react node or a class of some sort.
Also, for your example, a good idea would be to split the state of the task container, from that of the task itself. Having the container state being just the array, and each task item handling it's own state (title and description). This would give I think greater clarity to what you're trying to achieve.
But, conventions and clarity aside, I'd recommend that you follow the React hands-on tutorial to go through a whole bunch of concepts and good practices that may be of help and will save you a lot of time of try and error (at least this works for me)
I have created a dynamic form in React JS which adds new fields upon clicking the plus button. However when I try to update the form values using the onChange event it overwrite or sometimes deletes the old array. Why is this happening? What is the correct way to dynamically add values to an array?
Below is a screenshot of the error as shown in the console. The array adds the details of the initial person very well but after that when I try to enter "Person 1 details" the old array is somehow cleared. As soon as I enter contact number for Person 1 the array is again cleared.
My code can be found here: https://codesandbox.io/s/awesome-paper-kb7fgf?file=/src/Register.js
This is not the right approach you should not insert JSX elements into state. It is better to insert data in state and then derive UI from it.
The reason that could be causing your problem could be related to stale closures because when you store the JSX in state together with change handlers, the versions of change handlers that get stored belong to that render, hence they capture the formData with values from that render.
Anyway like I said you should discard that approach. I will show you below code how you should approach that problem but you will have to expand this to your use case, this is minimal use case. In this example you can add many persons and each person has input field for name property.
import React from 'react';
import './style.css';
import { v4 as uuidv4 } from 'uuid';
export default function App() {
let [persons, setPersons] = React.useState({
[uuidv4()]: {
name: 'john',
},
});
console.log(persons);
return (
<div>
<button
onClick={() => {
setPersons({
...persons,
[uuidv4()]: {
name: '',
},
});
}}
>
Add new person
</button>
{Object.entries(persons).map(([k, v]) => {
return (
<div key={k}>
<div>Person </div>
<input
value={v.name}
onChange={(e) => {
setPersons({
...persons,
[k]: {
...persons[k],
name: e.target.value,
},
});
}}
></input>
</div>
);
})}
</div>
);
}
I cannot understand why we set the value={this.state.task} when it is just an empty string, and how exactly the flow of data goes from the input value and then to the state.
When we first set the value, it's basically an empty string. But when I try to actually set value='' , the input field does not function properly on the rendered page.
I get that onChange we set the state to the corresponding name and value, and that that's how the data is flowing into the state. But then why does it not work when, again, we just set value='' ?
import React, { Component } from 'react';
import uuid from 'uuid/v4';
export class NewTodoForm extends Component {
constructor(props) {
super(props)
this.state = {
task: ""
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e){
this.setState({
[e.target.name]: e.target.value
})
}
handleSubmit(e){
e.preventDefault();
this.props.createTodo({ ...this.state, id: uuid() });
this.setState({ task: "" });
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label htmlFor='task'>New Todo</label>
<input
type='text'
placeholder='New Todo'
id='task'
name='task'
// why is this {this,state.task} ?
value={this.state.task}
onChange={this.handleChange}
/>
<button>Add Todo</button>
</form>
</div>
)
}
}
export default NewTodoForm
Because value is setting ... well the value of the input. By doing this value={this.state.task} basically you are connecting your input with the component's state and with the lifecycle of the React component. So basically whenever you change your component's state that has the input from anywhere (even programmatically), React will be able to update the input correctly and there won't be any bugs or weird stuff happening.
In the React docs it is explained very well. They are doing this controlled component ...
An input form element whose value is controlled by React in this way is called a “controlled component”.
... so that the state of the React component to be the only 'source of truth', meaning to prevent weird bugs and undesired behaviour.
Since the value attribute is set on our form element, the displayed value will always be this.state.value, making the React state the source of truth.
It is always a good practice to have one source of truth and not many. In this case if you leave the input's value to be different from the component's state, you are making more than one source of truths and thus exposing your app to bugs.
I am trying to create a custom widget for Netlify CMS that connects to the redux store to be updated when a file widget has a new path. My widget will then determine the size of the file to set it's own value.
So far, I have simply cloned the starter widget and changed src/Conrol.js to this:
import React from 'react';
import { connect } from 'react-redux' //Added this line
class Control extends React.Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
forID: PropTypes.string,
value: PropTypes.node,
classNameWrapper: PropTypes.string.isRequired,
}
static defaultProps = {
value: '',
}
render() {
const {
forID,
value,
onChange,
classNameWrapper,
} = this.props;
return (
<input
type="text"
id={forID}
className={classNameWrapper}
value={value || ''}
onChange={e => onChange(e.target.value)}
/>
);
}
}
//Added everything below
const mapStateToProps = (state, ownProps) => {
const fileUrl = "test";
return { value: fileUrl };
};
export default connect(mapStateToProps, null, null, { withRef: true })(Control);
With these changes I get the following error:
Element ref was specified as a string (wrappedInstance) but no owner
was set. This could happen for one of the following reasons: 1. You
may be adding a ref to a functional component 2. You may be adding a
ref to a component that was not created inside a component's render
method 3. You have multiple copies of React loaded See
https://reactjs.org/link/refs-must-have-owner for more information.
I can't figure out what I'm doing wrong. As for the reasons given in the error:
I'm working with a class component.
I didn't even touch the component rendering. I only added the connect().
I ran npm ls react as suggested at the link provided in the error. I got two copies of react, but one was "deduped".
I have searched far and wide for an answer, but have not found one. I am new to react/redux, so I'm hoping someone can point me in the right direction.
I'm unable to understand why this happens
I'm using React to create a web app
I've a Javascript object called user_info
var user_info = {
username: '',
password: '',
f_name: ''
};
Now I want to assign these values to the ones that I fetch from my firebase Realtime Database.
db.ref("/users").on("value", snapshot => {
alert("Firebase " + snapshot.child("username").val()) // Got the value correctly.....
user_info.username = snapshot.child("username").val();
user_info.password = snapshot.child("password").val(); //Assigning it to the object...
user_info.f_name = snapshot.child("f-name").val();
alert("Firebase Username = " + user_info.username); //Assigned Successfully...
});
After this block of code (outside the snapshot function), I use the alert() to display the username again.
alert(user_info.username); // No value is displayed here.
I guess that the value from the snapshot is not assigned to the object user_info. Later I'm exporting this object and importing it in another file where I face the same problem.
export {user_info};
--- In the Other file ---------
import React from 'react';
import {user_info} from './users.js';
function LandingPage()
{
return(
<div className="container">
<h1>Welcome {user_info.username}</h1> // Only 'Welcome' is displayed
</div>
);
}
export default LandingPage;
I can't understand why the value is not assigned to the Object user_info. Please correct my code so that I could store the value in my object.
Thank You.
value is updated but component is not rerendered use state (useState hook) here then render it from state.
and use useEffect hooks for updating state when "user_info" is updated.
import React,{useState,useEffect} from 'react';
import {user_info} from './users.js';
function LandingPage()
{
const [userInfo,setUserInfo]=useState({});
useEffect(()=>{
setUserInfo(user_info);
},[user_info]);
return(
<div className="container">
<h1>Welcome {userInfo?.username}</h1> // Only 'Welcome' is displayed
</div>
);
}
export default LandingPage;
https://reactjs.org/docs/state-and-lifecycle.html