Passing Parameters to react-router-dom Link Dynamically - reactjs

I am working on a React project where in one of the components I am passing properties to another component through react-router-dom Link like so:
let updateEmpInfo;
...
<Button component={Link}
halfWidth
variant="contained"
color="primary"
preventDefault
onClick={getEmpInfo}
to={{pathname: "/update",
state:
{id: updateEmpInfo.id,
name: updateEmpInfo.name,
department: updateEmpInfo.department,
gender: updateEmpInfo.gender}
}}>Update information
</Button>
My questin is specifically about the "state" property. As you can see, I am passing several state parameters (id, name, department and gender). Since Link component is inside the render method, it requires the updateEmpInfo variable to be defined somewhere in the render method. I am trying to take input from user and based on their input set the value of all updateEmpInfo properties, after the component has rendered. All of them will be passed to the state property of Link. I am trying to do so in the getEmpInfo function. However, regardless of the input, the state property preserves all initial values that were set during the render. Is there a way to change updateEmpInfo properties based on the user input once the Link is clicked? I my question is clear enough. I will be happy to provide any additional info. Thank you in advance!

If I understood you correctly, the values passed are not how the user adjusted them.
I'd suppose it's an onChange-Listener missing to the InputFields or an onSubmit to the whole form respectively.
import { Component } from 'react';
import { withRouter } from 'react-router-dom';
class MyEmployeeForm extends Component {
constructor(props) {
super(props);
this.state = {
id: "",
name: "",
department: "",
gender: ""
};
}
onChange = (event) => {
this.setState({[event.target.name]: event.target.value});
}
onUpdate = (event) => {
event.preventDefault();
this.props.history.push({
pathname: '/update',
state: this.state});
}
render() {
<form>
<input type="text" name="id" placeholder="id" value={this.state.id} onChange={this.onChange} />
<input type="text" name="name" placeholder="name" value={this.state.name} onChange={this.onChange}/>
<input type="text" name="department" placeholder="department" value={this.state.department} onChange={this.onChange}/>
<input type="text" name="gender" placeholder="gender" value={this.state.gender} onChange={this.onChange}/>
</form>);
<Button component={Link}
halfWidth
variant="contained"
color="primary"
onClick={this.onUpdate}>
Update information
</Button>
}
}
export default withRouter(MyEmployeeForm);
see here Get form data in ReactJS ,
How to pass params with history.push/Link/Redirect in react-router v4?

Related

React not re-rendering component when setState is called from the method which is called on a button click

I am trying to make the values in object empty once Login button is clicked after alerting the message. But that is not changing the value of useState object and hence not re-rendering the component. Can anyone please help me with this?
import React from 'react'
import { useState } from 'react';
function Login() {
const [user, setUser] = useState({
prn: "",
password: ""
});
const doLogin = () => {
alert("Welcome " + user.prn + " " + user.password);
setUser(
{
prn: "",
password: ""
}
)
console.log(user);
}
const onInputChange = (event) => {
// code to change state
}
return (
<div className='login'>
<input
type="number" name="prn"
defaultValue={user.prn}
onChange={onInputChange}
/>
<br /> <br />
<input
type="password" name="password"
defaultValue={user.password}
onChange={onInputChange}
/>
<br /> <br />
<input type="button" value="Login" onClick={doLogin} />
</div>
);
}
export default Login
Your inputs are not controlled, so your state value updates are not reflected. To fix this, add value prop to your input like so:
<input
type="number"
name="prn"
value={user.prn}
onChange={onInputChange}
/>
<br /> <br />
<input
type="password"
name="password"
value={user.password}
onChange={onInputChange}
/>
Edit 1: I added the onChange handler in the sandbox, and adding it here as well
const onInputChange = (event) => {
// code to change state
setUser((prev) => ({
...prev,
[event.target.name]: event.target.value
}));
};
Here's a sandbox as well
First, let's address the issue of the state value seemingly not updating.
Setting the state in React acts like an async function.
Meaning that the when you set the state and put a console.log right after it, it will likely run before the state has actually finished updating.
Which is why we have useEffect, a built-in React hook that activates a callback when one of it's dependencies have changed.
Example:
useEffect(() => {
console.log(user)
// Whatever else we want to do after the state has been updated.
}, [user])
This console.log will run only after the state has finished changing and a render has occurred.
Note: "user" in the example is interchangeable with whatever other state piece you're dealing with.
Check the documentation for more info about this.
Second, as for the reason the values of the inputs in the UI are not updating when you set the state, is likely due to using the defaultValue attribute on the inputs.
Try using the regular value attribute instead.

Difference between React controlled component and uncontrolled component

I am new to react and while learning I come across this example of controlled component.
function App() {
let [fName, setFName]=useState('');
return (
<div className="container">
<h1>Hello {fName }</h1>
<input name ='fname' value={fName} onChange={(e)=> setFName(e.target.value)} type="text" placeholder="What's your first name?" />
</div>
);
}
just adding value={fName} makes is controlled . I don't actually understand what does it mean by controlled component and uncontrolled. Can you explain it from beginner prospective.
An uncontrolled component means that you will let the component itself manage the value. It's own internal mechanism will keep track of it.
Now when you add the value property to the input component, you will start to "control" the component yourself. The value you put into that property, will be the value that will be displayed.
You can literally control the value yourself, by just passing it in as is, or by changing the value before passing it in.
Controlled Components
These components have what is called a callback function that is triggered each time we enter something new in the form element.
Triggering the function will typically store or update what we type in the same React component that displays the form element that was used
Most widespread use it with forms
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Unontrolled Components
These components such as <input> typically maintain their own state and update it based on user input.
In other words, they will accept what we type and have the responsibility of remembering it, and in order to retrieve the values they remembered, you have to get it when you need it.
The latter usually happens during form submission. They can be classified under uncontrolled components.
class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.input = React.createRef();
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.current.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Here, Reactjs documentation provided explanation.
A Controlled Component is one that takes its current value through props and notifies changes through callbacks like onChange. A parent component "controls" it by handling the callback and managing its own state and passing the new values as props to the controlled component. You could also call this a dumb component/stateless component.
An Uncontrolled Component is one that stores its own state internally, and you query the DOM using a ref to find its current value when you need it. This is a bit more like traditional HTML.
React form components support both controlled and uncontrolled usage:
// Uncontrolled:
<input type="text" defaultValue="hey" ref={inputRef} />
// Controlled:
<input type="text" value={this.state.value} onChange={onHandleChange} />

In JSX | React How can the prop be passed to the button state so the button state can load a new page?

Trying to go from the search using the react datepicker and then check available dates. Having some trouble passing the props to the btn so I can create the new page based on the date selected.
Everything is Imported correctly we can leave that out below is my Datapicker Component and the Btnsearch Component I need the pops to be used by.
I have tried to pass the prop in every element on the component, with the btn built in to the component. I have read its better to pass the props down the chain of elements. To children so I tried to do that lastly. Still can't seem to catch the Datepicked to even console.log it.Tried to build a alert as well.
class Datepicker extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ""
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
alert("A name was submitted: " + this.state.value);
event.preventDefault();
}
state = {};
render() {
console.log(this.props.value);
return (
<>
<FormGroup>
<InputGroup className="input-group-alternative">
<InputGroupAddon addonType="prepend">
<InputGroupText
>
<i className="ni ni-calendar-grid-58" />
</InputGroupText>
</InputGroupAddon>
<ReactDatetime
value={this.state.value}
onChange={this.handleChange}
inputProps={{
placeholder: "Date Picker Here"
}}
timeFormat={false}
/>
</InputGroup>
</FormGroup>
<Btnsearch />
</>
);
}
}
export default Datepicker;
class Btnsearch extends React.Component {
render() {
return (
<button value={this.props.value} className="btn btn-success search-card-btn">Search</button>
);
}
};
export default Btnsearch;
I expect to console.log and alert the props been changed when the button is clicked in order to populate a new page. I get props undefined
Just pass down the prop:
<Btnsearch value={this.state.value}/>
Otherwise your component would never get it.
Edit: there was several issues in your code, I made a snippet for you to check.
https://stackblitz.com/edit/react-af37vl
Select a date, hit "search" and date will be logged in console.
Notice (from react-datetime docs):
Callback trigger when the date changes. The callback receives the
selected moment object as only parameter, if the date in the input is
valid. If the date in the input is not valid, the callback receives
the value of the input (a string).

How access specific DOM in React js

I'm trying to get input tags' HTML inner values when submitting a form.
private handleSubmit = (event: any) => {
event.preventDefault();
console.log(event.currentTarget);
};
When the submit the form, it calls the function handleSubmit and it console logs the following.
Under the form tag, the first div has username value and the second div has password value. I would like to acess the two values. I think I should use DOM to do that, but can't be sure if I'm going for the right direction cuz I found some postings saying using DOM is not recommended.
Can anyone explain how I can acheive this?
Ideally you should update your state as the user enters information, and then access the data from the state. This would also allow you to run any validation on the data prior to it going into the state if you'd like.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
username: null,
password: null
}
this.submitForm = this.submitForm.bind(this);
this.updateState = this.updateState.bind(this);
}
updateState (e) {
this.setState({[e.target.name]: e.target.value})
}
submitForm (e) {
e.preventDefault();
console.log(this.state);
}
render() {
return (
<div className="App">
<form onSubmit={this.submitForm}>
<input type="text" name="username" placeholder="username" onChange={this.updateState} /><br />
<input type="password" name="password" placeholder="password" onChange={this.updateState} /><br />
<button type="submit">Submit</button>
</form>
</div>
);
}
}
export default App;
The above code does the following:
Stores default values for username and password. While this isn't required, it makes the code more readable
binds this to functions that need to access state
Uses an updateState() function that is called onChange of the inputs
updateState uses the name attribute of the input as the key for the state
You could customize the updateState() function to do some validation, before saving to state if you'd like.
Excessive Rendering
ReactJS is pretty smart to no re-render the REAL DOM if your render() method doesn't actually rely on the state values that were updated; however, if you'd like to prevent ReactJS from even creating the Virtual DOM and comparing, you could utilize the shouldComponentUpdate() lifecycle hook.
In the particular example above, since render doesn't rely on ANYTHING in state, you could simply add the following method:
shouldComponentUpdate(prevState, nextState) {
return false;
}
That will prevent the render method from EVER re-rendering, which is most likely not going to work in a normal component, thus you could do something like this instead, only re-rendering on values you care about.
shouldComponentUpdate(prevState, nextState) {
if (nextState.email !== prevState.email) {
return true
}
return false;
}
Demo
https://repl.it/#AnonymousSB/SO53689072
If you want to use the DOM, once you have the form element (event.currentTarget in your case), you can use the .elements property to access a list of child inputs and buttons.
Alternatively, you can use React refs to keep track of the underlying HTML element when it's rendered.
render() {
return ... <input ref={(e) => this._name = e; } ....> ... ;
}
handleSubmit(e) {
var name = this._name ? this._name.value : '';
....
}
This can achieve what you want to
class Login extends Component{
state={
username:"",
password:""
}
onChange = (event)=>{
event.preventDefault()
this.setState({
[event.target.name]: event.target.value})
}
onSubmit = (event)=>{
event.preventDefault()
// submit whatever is in state from here
console.log(this.state)
}
render(){
return(<div>
<form onSubmit={handleSubmit}>
<input type="text" name="username" onChange={this.onChange} /><br />
<input type="password" name="password" onChange={this.onChange} /><br />
<button type="submit">Submit</button>
</form>
</div>)
}
}

react bootstrap readonly input within formcontrol

I am using react bootstrap and this framework provides some nice FormControls.
But I would like to make the Input field that is generated within the FormControls to have a prop of readonly="readonly". This way, this field looks the same as my other FormControls, but does not give a keyboard input on IOS.
In my case, the input will be provided by a calendar picker which will be triggered by an popover.
Does anyone know how to give FormControl the parameter readonly="readonly", so that the generated Input field in the browser will have the prop readonly="readonly"?
Many thnx for the answers!
It doesn't look like a problem with react-bootstrap, but rather with react itself.
React is not transferring the 'readonly' prop to the generated (real) DOM element:
React-bootstrap create the following react virtual dom input:
Yet, react generated the following real DOM element, omitting the readonly attribute:
Maybe using 'disabled' could help in your case:
<FormControl
disabled
type="text"
placeholder="Enter text"
onChange={this.handleChange}
/>
For differences between readonly & disbabled see here:
https://stackoverflow.com/a/7730719/1415921
I have created an issue in React's github repo: #6783
UPDATE
After getting an answer in the above issue. You need to write it with camelcase: readOnly.
So it should be:
<FormControl
readOnly
type="text"
placeholder="Enter text"
onChange={this.handleChange}
/>
Old problem, new approach: Take advantage of onChange event to control if you'll call handleChange event or not. I defined editForm as a props value controlled by buttons, to see if i'm in view or edit mode.
Example:
<TextField
name="id"
label="ID
value={entityState.entity.Id || ""}
onChange={(a) => (props.formEdit ? handleChange(a) : "")}
/>
On the basis of values this attribut will be readOnly={!!value} to make input field disable to edit
class Input extends React.Component {
render () {
const { defaultValue, value, onChange } = this.props
const nothing = () => {}
return (
<input
type='text'
defaultValue={defaultValue}
value={value ? value.toUpperCase() : undefined}
readOnly={!!value}
onChange={value ? nothing : onChange}
/>
)
}
}
class App extends React.Component {
constructor () {
super ()
this.state = {
value: 'arr'
}
}
handleChange (e) {
const { target: { value }} = event
this.setState({ value })
}
render () {
const { value } = this.state
return (
<div>
<Input
onChange={this.handleChange.bind(this)}
defaultValue={'patata'}
/>
<Input
value={value}
/>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('arr'))

Resources