Currently, the default value of my input field is 1. If I try to type something in the input field, nothing changes.
interface Orders {
order_graph_1: number;
order_graph_2: number;
}
interface MyProps extends Orders {
setOrders: (...args: any) => void; // function which takes args...??
}
interface MyState extends Orders {
//otherProperty: string;
}
class Setup extends React.Component<MyProps, MyState>{
state = {
order_graph_1: this.props.order_graph_1,
order_graph_2: this.props.order_graph_2
};
// needs to be an arrow function to access `this` properly
// could use ( event: React.ChangeEvent<HTMLInputElement>)
// could avoid the assertion by passing the name as an argument
setOrders = (event: any) => {
this.setState((prevState) => ({
...prevState,
[event.target.name]: parseInt(event.target.value)
}));
};
render(){
return(
<div className="row">
<div className="col-6">
<p className="text-center">Order of first model: </p>
<div className="w-100 d-flex justify-content-center">
<input className="text-center" name="order_graph_1" type="number" value={this.props.order_graph_1} onChange={this.setOrders.bind(this)} min="1" max="10"/>
</div>
</div>
</div>
);
}
}
export default Setup;
To test, I canged the onChange function
onChange={()=>console.log("hello")}
everytime I tried to type in the input field, I saw hello being printed in the console but the value of the input field still does not change.
edit:
This was a JS code (https://github.com/MenesesGHZ/polynomial-regression-js):
class RegressionSetup extends React.Component{
constructor(props){
super(props);
this.orders = {
"order_graph_1":this.props.order_graph_1,
"order_graph_2":this.props.order_graph_2
};
}
setOrders(event){
this.orders[event.target.name] = parseInt(event.target.value);
this.props.setOrders(Object.values(this.orders));
}
render(){
return(
<div className="row">
<div className="col-6">
<p className="text-center">Order of first model: </p>
<div className="w-100 d-flex justify-content-center">
<input className="text-center" name="order_graph_1" type="number" value={this.props.order_graph_1} onChange={this.setOrders.bind(this)} min="1" max="10"/>
</div>
</div>
<div className="col-6">
<p className="text-center">Order of second model: </p>
<div className="w-100 d-flex justify-content-center">
<input className="text-center"name="order_graph_2" type="number" value={this.props.order_graph_2} onChange={this.setOrders.bind(this)} min="1" max="10"/>
</div>
</div>
</div>
);
}
}
export default RegressionSetup;
Upon changing the value of input, a line on a graph changed according to the value. I had to change this code to Typescript. This is what I have now.
interface Orders {
order_graph_1: number;
order_graph_2: number;
}
interface MyProps extends Orders {
setOrders: (...args: any) => void; // function which takes args...??
}
interface MyState extends Orders {
//otherProperty: string;
}
class Setup extends React.Component<MyProps, MyState>{
state = {
// it is best not to derive state from props
order_graph_1: this.props.order_graph_1,
order_graph_2: this.props.order_graph_2
};
// needs to be an arrow function to access `this` properly
// could use ( event: React.ChangeEvent<HTMLInputElement>)
// could avoid the assertion by passing the name as an argument
setOrders = (event: any) => {
// I don't love this solution, but we can avoid the TS errors by copying the previous state
this.setState((prevState) => ({
...prevState,
[event.target.name]: parseInt(event.target.value)
}));
};
render(){
return(
<div className="row">
<div className="col-6">
<p className="text-center">Order of first model: </p>
<div className="w-100 d-flex justify-content-center">
<input className="text-center" name="order_graph_1" type="number" value={this.state.order_graph_1} onChange={this.setOrders.bind(this)} min="1" max="10"/>
</div>
</div>
{/* <div className="col-6">
<p className="text-center">Order of second model: </p>
<div className="w-100 d-flex justify-content-center">
<input className="text-center"name="order_graph_2" type="number" value={this.props.order_graph_2} onChange={this.setOrders.bind(this)} min="1" max="10"/>
</div>
</div> */}
</div>
);
}
}
export default Setup;
although it compiles without an error, the input value thing is not working. It does not change the line on the graph so I am assuming the state is not saved. How can I fix this?
The problem is that you are using the value of order_graph_1
and order_graph_2
that you get from props but updating the ones that you have in state and not updating the ones in the props.
The code that you are converting updates both the state and the props (by calling this.props.setOrders
). But it is totally unnecessary to have these variables in a component state of RegressionSetup
at all since it can already access and update them through props.
The setOrders
function which is passed down from the parent Main
component is also a bit silly because both components have a state with properties order_graph_1
and order_graph_2
-- so why are we passing them as a tuple to setOrders
?
In Main
you can delete the setOrders
function and instead pass down an arrow function that calls setState
.
<RegressionSetup
order_graph_1={this.state.order_graph_1}
order_graph_2={this.state.order_graph_2}
setOrders={(orders) => this.setState(orders)}
/>
setState
in a class component takes a partial new state and merges it with the current state. That means the we don't even have to pass both orders to setOrders
. We can call it with just one order and that's fine.
So we can define the props for our RegressionComponent
like this:
export interface OrderGraphs {
order_graph_1: number;
order_graph_2: number;
}
interface Props extends OrderGraphs {
setOrders(graphs: Partial<OrderGraphs>): void;
}
There is no reason for it to be a class component, but it also doesn't matter. RegressionSetup
does not need to use state, hooks, or lifecycle methods. It just takes props and returns JSX.
I still prefer to render the two inputs using a function since it's less repetition but obviously you don't have to do that. We use the variable property
to get the correct property from props value={this.props[property]}
and to set the correct key on the object that we pass to setOrders
: onChange={(e) => this.props.setOrders({[property]: parseInt(e.target.value, 10)})}
class RegressionSetup extends React.Component<Props> {
renderInput(property: keyof OrderGraphs, label: string) {
return (
<div className="col-6">
<p className="text-center">{label}</p>
<div className="w-100 d-flex justify-content-center">
<input
className="text-center"
name={property}
type="number"
value={this.props[property]}
onChange={(e) =>
this.props.setOrders({ [property]: parseInt(e.target.value, 10) })
}
min="1"
max="10"
step="1"
/>
</div>
</div>
);
}
render() {
return (
<div className="row">
{this.renderInput("order_graph_1", "Order of first model: ")}
{this.renderInput("order_graph_2", "Order of second model: ")}
</div>
);
}
}
I messed with the other components as well. I added Props
and State
types for everything. I also used arrow functions to get rid of all the bind(this)
calls. There are still a few Typescript errors related to the external packages (chart.js and mathjs).