How to use props to update my form Reactjs - reactjs

I have an Update Dialog Component which needs to update some values. My update dialog looks like following.
class EditWebsiteComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
handleChange = name => event => {
let temp = this.props.selectedWebsite;
temp[name] = event.target.value;
console.log(temp);
this.props.changesSelectedWebsite(temp);
// this.setState({
// [name]: event.target.value,
// });
};
onFileChange = event => {
this.setState({ logo: event.target.files[0] });
};
render() {
const openDialog = this.props.openDialog;
const {selectedWebsite} = this.props;
return (
<div>
{/*{this.props.selectedWebsite && this.props.selectedWebsite.title}*/}
<Dialog
fullWidth={true}
open={openDialog}
onClose={this.props.closeDialog}
>
<DialogTitle>
{"Edit Website"}
</DialogTitle>
<DialogContent>
<FormControl className="w-100">
<TextField
style={{'margin-right': "10px"}}
className="col-md-11 col-11"
id="name"
label="Title"
value={selectedWebsite.title}
onChange={this.handleChange('title')}
margin="normal"
fullWidth
/>
<TextField
style={{'margin-right': "10px"}}
className="col-md-11 col-11"
id="name"
label="URL"
value={selectedWebsite.url}
onChange={this.handleChange('url')}
margin="normal"
fullWidth
/>
<div style={{"margin-top": "20px"}} className='col-md-11 col-11 flex-class-custom'>
<input type="file" onChange={this.onFileChange} />
</div>
</FormControl>
</DialogContent>
<DialogActions>
<Button onClick={this.props.closeDialog} color="secondary">
Close
</Button>
<Button onClick={() =>
this.props.editWebsite({title: selectedWebsite.title, url:selectedWebsite.url, logo:selectedWebsite.logo, id:selectedWebsite.id})
} color="primary">
Edit
</Button>
</DialogActions>
</Dialog>
{this.props.showMessage && NotificationManager.error(this.props.alertMessage)}
<NotificationContainer/>
</div>
)
}
}
const mapDispatchToProps = dispatch => ({
editWebsite: (payload) => dispatch(editWebsite(payload)),
changesSelectedWebsite: (payload) => dispatch(changesSelectedWebsite(payload))
});
const mapStateToProps = state => ({
selectedWebsite : state.Websites.selectedWebsite,
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(EditWebsiteComponent)
I am getting the current values from selected website and on change i am updating the props by dispatching the changesSelectedWebsite action, but the dialog form is not being updated, although props are being updated. I am quite new to react and redux so i wanted to ask if this approach is right ? and how else can i achieve this because i need values of selectedWebsites and also need to update if user changes anything.

Can you try changing this line
this.props.changesSelectedWebsite(temp);
to
this.props.changesSelectedWebsite({...temp});
As long as you are getting the prop value as is without "recreating it", the state wont feel the change, so, {...temp} recreates the object, why? because it tells the state that this is a new object and triggers a rerender and saves the new state.

Related

React - how to invoke popup window in my case?

I'm not a React expert yet thus I have a question for you - how to invoke my popup window from:
import {options, columns,convertToArray} from './consts'
const index = () => {
const {data, loading, error, performFetch} = fetchHook({path: "/xxx/yyy", fetchOnMount: true})
return (
<div className={classes.Container}>
<h1>List of products</h1>
<Divider className={classes.Divider} />
<ProductTable data={convertToArray(data)} options={options} columns={columns}/>
</div>
)
}
export default index;
consts.js
export const actions = (productPropertyId, showModal) => {
const productDetails = (productPropertyId) => {
}
const removeProduct = (productPropertyId, showModal) => {
actions(productPropertyId, showModal);
return (
<div className={classes.actionsContainer}>
<Button
onClick={() => productDetails(productPropertyId)}
> {"More"}
</Button>
<Button
const removeProduct = (productPropertyId, showModal) => {
actions(productPropertyId, showModal);
>{"Remove"}
</Button>
</div>
)
};
export const convertToArray = (productList) => {
let products = []
if (productList != null) {
productList.map(product => {
column1, column2, column3, actions(product.id)]
products.push(prod)
})
}
return products;
};
My popup is --> <FormDialog/> based on react Materials.
Is it possible to invoke popup in this place?
I have a react material table with some columns. The last column contains 2 buttons, one of them is "Remove" (row). Here I want to invoke my popup. Maybe I should rebuild my structure?
UPDATE
Below is my popup - I wonder how to run this popup from the place above:
const formDialog = (popupOpen) => {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
{/*<Button variant="outlined" color="primary" onClick={handleClickOpen}>*/}
{/* Open alert dialog*/}
{/*</Button>*/}
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Subscribe</DialogTitle>
<DialogContent>
<DialogContentText>
To subscribe to this website, please enter your email address here. We will send updates
occasionally.
</DialogContentText>
<TextField
autoFocus
margin="dense"
id="name"
label="Email Address"
type="email"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleClose} color="primary">
Subscribe
</Button>
</DialogActions>
</Dialog>
</div>
);
}
export default formDialog;
UPDATE 2
I updated my code taking into cosideration the tips from your response, see above. Can I add a parameter showModal in my export const actions = (productPropertyId, showModal) and then invoke this component with different showModal value? UNfortunately my popup doesn't appear when I click on Remove button :(
You can invoke it conditionally and controle it using some state variable. Like this:
const [removeModal, removeToggle] = useState(false);
return (<>
<div className={classes.actionsContainer}>
<Button
onClick={() => productDetails(productPropertyId)}
> {"More"}
</Button>
<Button
onClick={() => removeToggle(true)}
>{"Remove"}
</Button>
</div>
{removeModal ? <YourComponent /> : ''}
</>
)
I'm using a react fragment there <></> just to position the modal div outside the main div, but you can also invoke it inside your main div as well (I usually do this and set the position: fixed so the modal/popup coud appear in top of everything).

call a component from an action- react

I am using functional components in my application, initially it had a component with a lot of code and I divided it into 3 to be able to better organize the code and have reusable components, the first one contains a <MaterialTable>, the second one <dialog> and the third one <form>.
In the component where the table is located import the <dialog>, within the <dialog> component import the <form>, that way the form is inside the dialog box and the dialog box I want to send it to call from the actions of the board.
The problem is that when adding the component in actions I get the following error
Expected an assignment or function call and instead saw an expression
How can I open the component from the actions in the table?
Table
export default function User(){
const[user, setUser]= useState({Users:[]});
useEffect(()=>{
const getUser=async()=>{
const response =await axios.get('/api/users');
setUser(response.data);
console.log(response.data)
}
getUser();
},[]);
return(
<div>
<MaterialTable
title="Users"
columns={[
{ title: 'Code', field: 'code' , type: 'numeric'},
{ title: 'Name', field: 'name' },
{ title: 'Lastname', field: 'lastname' },
{ title: 'Age', field: 'age', type: 'numeric'},
]}
data={user.Users}
actions={[
{
icon: 'event',
tooltip: 'Agregar cita',
onClick:(event, rowData)=>{
//event.preventDefault();
<Dialogs/>
}
}
]}
/>
</div>
);
}
Dialog
function Dialogs(){
const [open, setOpen] = useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return(
<div>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Subscription></DialogTitle>
<DialogContent>
<DialogContentText>
Subscription
</DialogContentText>
<AddSuscription/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
</DialogActions>
</Dialog>
</div>
)
}
export default Dialogs;
Form
export default function AddSuscription(props){
const initialState={code:0, email:'', alias:''}
const[subscription, setSubscription]=useState(initialState);
const handleChange=(event)=>{
setSubscription({...subscription,[event.target.name]:event.target.value})
}
const handleSubmit=(event)=>{
event.preventDefault();
if(!subscription.code || !subscription.email || !subscription.alias)
return
const postSubscription=async()=>{
try {
axios.post('/api/Subscription/add',subscription);
props.history.push('/Subscription');
}
catch (error) {
console.log('error', error);
}
}
postSubscription();
}
return(
<div>
<form onSubmit={handleSubmit} >
<TextField
id="filled-name"
name="code"
label="Code"
value={subscription.code}
onChange={handleChange}
margin="normal"
/>
<TextField
id="filled-name"
label="Email"
value={subscription.email}
name="email"
onChange={handleChange}
margin="normal"
/>
<TextField
id="filled-multiline-static"
label="Alias"
value={subscription.alias}
name="alias"
onChange={handleChange}
margin="normal"
/>
<Button
variant="contained"
color="primary"
type="submit">
Add
</Button>
</form>
<div>
);
}
onClick:(event, rowData)=>{
<Dialogs/>
}
You cannot render from an event handler like this. Where exactly would you expect that component to appear? React just doesn't work that way.
Instead, keep some state, change that state (which automatically re-renders your component), then conditionally render what you want based on that state.
Something like this:
Add state to the main component that needs to render the dialog:
export default function User() {
const [showDialog, setShowDialog] = useState(false)
//...
Change your event handler to change that state
onClick:(event, rowData)=>{
setShowDialog(true)
}
And in your main render, conditionally render the <Dialogs> component.
return(
<div>
{showDialog && <Dialogs/>}
<MaterialTable
{/* ... */}
/>

How do you access the values from a redux form in a reducer?

I am trying to get access from the redux form that is being filled out inside my reducer so I can replace the values of another object to an array. How do you properly submit and pass the values from a redux form?
I have tried passing it back through the redux actions. I've tried accessing it in the reducer directly from the store because I figured that's where it's being stored. I feel like I am doing something simple wrong
class EditWorkoutItem extends Component {
state = {
open: false
};
// Opens the page
handleClickOpen = () => {
this.props.loadData(this.props.id);
this.setState({ open: true });
};
// Cancels the changes and closes the page
handleClose = () => {
this.setState({ open: false });
};
// Passes back the id to the parent so the correct item can be replaced.
// Passes back the new workout list
handleSubmitChanges = e => {
e.preventDefault();
this.props.editWorkout(this.props.id); // this passes the workouts id back
this.setState({ open: false });
};
render() {
return (
<>
<Button
color="primary"
size="small"
disableRipple
onClick={this.handleClickOpen}
>
edit
</Button>
<Dialog
open={this.state.open}
onClose={this.handleClose}
style={styles.dialog}
fullScreen
>
<DialogTitle>Edit This Workout</DialogTitle>
<form onSubmit={this.props.handleSubmit} style={styles.form}>
<DialogContent>
<Field
name="date"
component={DatePicker}
format={null}
hintText="Date Of Workout"
fullWidth={true}
locale="en-US"
/>
<Field
name="name"
component={TextField}
floatingLabelText="Workout Name"
style={styles.textfield}
/>
<Field
name="duration"
type="number"
component={TextField}
floatingLabelText="Estimated Duration"
style={styles.textfield}
/>
<FieldArray name="exercises" component={renderExercises} />
</DialogContent>
<DialogActions>
<Button
color="primary"
type="submit"
onClick={this.handleSubmitChanges} //Submitted here
>
Submit Changes
</Button>
<Button onClick={this.handleClose}>Cancel</Button>
</DialogActions>
</form>
</Dialog>
</>
);
}
}
this is the reducer:
case "EDIT_WORKOUT":
return (state = {
...state,
workoutlist: state.workoutlist.map(workout => {
// Find the item with the matching id
if (workout.id === action.payload.id) {
// Return a new object
return {
...workout, // copy the existing item
workout: action.values // replace the current workout
};
}
// Leave every other item unchanged
return workout;
})
});
No matter what I do, the values are not getting to the reducer. Any help would be seriously appreciated!!!
Have you ever try to use mapDistchpathToProps to dispatch action in Class EditWorkoutItem like this:
const connectToRedux = connect(null,
dispatch => ({
handleSubmitChanges: values => {
// values here is all value of your form
console.log(values);
dispatch({ type: "EDIT_WORKOUT", payload: values }) // dispatch an action with
// type is EDIT_WORKOUT and payload is all value of Redux-form
}
})
)
Call handleSubmitChanges in form:
<form onSubmit={this.props.handleSubmitChanges} style={styles.form}>
...
...
<Button
color="primary"
type="submit"
// onClick={this.handleSubmitChanges} // remove this
>
Now, in reducer you can get data in action from action creator with type EDIT_WORKOUT:
case "EDIT_WORKOUT":
console.log(action.payload) // You can receive values of Redux-form here
return;

React Passing data to components

I have a parent stateful component and i pass the state of show dialog to a stateless header component.
When a icon clicked on the header component it opens a stateless dialog component.
In the stateless dialog component i want to be able to enter data into a text-field.
Do i have to completely change my code to make the stateless dialog to a stateful component?
Below is my code. If anyone can recommend the best way of doing this. Thanks.
class Layout extends Component {
state = {
show:false
}
toggleSidenav = (action) =>{
this.setState({
showDialog:action
})
}
render(){
return(
<div>
<Header
showNav={this.state.showDialog}
onHideNav={() => this.toggleSidenav(false)}
onOpenNav={() => this.toggleSidenav(true)}
/>
</div>
)
}
}
export default Layout;
Header component
const Header = (props) => {
console.log(props.onOpenNav)
const navBars = () => (
<div>
<AppBar position="static">
<Toolbar>
<IconButton color="inherit" aria-label="createfolder">
<SvgIcon>
<path d={createfolder}
onClick={props.onOpenNav}
name="firstName" />
</SvgIcon>
</IconButton>
</Toolbar>
</AppBar>
</div>
)
return (
<div>
<SideNav {...props} />
<div>
{navBars()}
</div>
</div>
)
}
Dialog Component
const DialogBox = (props) => {
return (
<div>
<Dialog
open={props.showNav}
aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Add Folder</DialogTitle>
<DialogContent>
<TextField
margin="normal"
/>
</DialogContent>
<DialogActions>
<Button onClick={props.onHideNav} color="primary">
Cancel
</Button>
<Button onClick={props.onHideNav} color="primary"
onChange={this.handleFieldChange}
value={this.value}
>
Create
</Button>
</DialogActions>
</Dialog>
</div>
)
}
Since component Header is readily stateful. You could initialize its state to
state = {
show:false,
formData: {} //later, you may save user input from the child component here
}
and in Header component, you may add a function:
handleInputEntered = (event) => {
const _data = { ...this.state.formData };
_data[event.target.name] = event.target.value;
this.setState({
formData: _data
});
}
and make sure to pass this new function as a prop to like this:
<Header
handleInputEntered = {this.handleInputEntered}
/>
and set onChange to be this new function where you have input field:
<TextField
onChange={this.props.handleInputEntered}
/>
It seems you're using MaterialUI, so just look up how you may supply the onChange property to TextField component.
Is this clear?

ReactJS - How can I set a value for textfield from material-ui?

I have a selectField and I want to set a value on it. Let say I type on it and when I click a button, the button will call a function that will reset the value of the textfield?
<TextField hintText="Enter Name" floatingLabelText="Client Name" autoWidth={1} ref='name'/>
You can do it in this way
export default class MyCustomeField extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Enter text',
};
}
handleChange = (event) => {
this.setState({
value: event.target.value,
});
};
handleClick = () => {
this.setState({
value:'',
});
};
render() {
return (
<div>
<TextField
value={this.state.value}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>Reset Text</button>
</div>
);
}
}
It's maintained that the right way is to have the component be controlled in a scenario like the accepted answer there, but you can also control the value in this gross and culturally unacceptable way.
<TextField ref='name'/>
this.refs.name.getInputNode().value = 'some value, hooray'
and you can of course retrieve the value like so
this.refs.name.getValue()
Instead of using ref you should use inputRef
const MyComponent = () => {
let input;
return (
<form
onSubmit={e => {
e.preventDefault();
console.log(input.value);
}}>
<TextField
hintText="Enter Name"
floatingLabelText="Client Name"
autoWidth={1}
inputRef={node => {
input = node;
}}/>
</form>
)
};
Here is a short simple React Hook version of the answer
export default function MyCustomeField({
initialValue= '',
placeholder= 'Enter your text...'
}) {
const [value, setValue] = React.useState(initialValue)
return (
<div>
<TextField
placeholder={placeholder}
value={value}
onChange={e => setValue(e.target.value)}
/>
<button onClick={() => setValue(initialValue)}>Reset Text</button>
</div>
);
}
I would suggest to do a little wrap on the original MUI TextField.
export default function ValueTextField(props) {
const [value, setValue] = useState(props.value);
return (
<TextField {...props} onChange={(event) => setValue(event.target.value)} value={value}/>
);
}
Now you can use your own ValueTextField component now.
<ValueTextField value={"hello world"}></ValueTextField>
I prefer this way to assign the state variables:
<TextField value={mobileNumber}
onChange={e => { this.setState({ mobileNumber: e.target.value }) }}
className={classes.root}
fullWidth={true}
label={t('mobile number')} />

Resources