Updating state in react js - javascript

I have this code that renders a dropdown component from material ui and it's populated with data coming from a WS.
I set an initial value that is the firs element coming from the WS so when I render the page for the first time I can see the correct value in the dropdown.
My issue is when I try to select a different value on the dropdown, I'm not able to do it and I think is because I'm not updating the state, I have a method called "handleChange" but I'm missing something there but don't know what.
This is the code and hope someone can help with this, I'm new to react and still to practice much more.
import React, { Component } from 'react';
import DropDownMenu from 'material-ui/DropDownMenu';
import MenuItem from 'material-ui/MenuItem';
export default class WebserviceTest extends Component {
constructor() {
super();
this.state = {
data: [],
selected: ''
};
this.renderOptions = this.renderOptions.bind(this);
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
const url = 'https://randomuser.me/api/?results=4';
fetch(url)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
data: findResponse.results,
selected: findResponse.results[0].name.first
});
console.log('----- ', this.setState.selected);
});
}
handleChange(value) {
this.setState({ selected: (value) });
}
renderOptions() {
return this.state.data.map((dt, i) => {
return (
<MenuItem
key={i}
value={dt.name.first}
primaryText={dt.name.first} />
);
});
}
render() {
return (
<div>
<DropDownMenu value={this.state.selected} onChange={this.handleChange}>
{this.renderOptions()}
</DropDownMenu>
</div>
);
}
}
Any help will be very welcome!
Thanks in advance..

In material UI dropdown, the selected value appears as third argument. So use something like this for your handleChange method
handleChange(event, index, value) {
this.setState({ selected: (value) });
}
Ref: http://www.material-ui.com/#/components/dropdown-menu#properties

Related

React.js Material-Table pass selected rows to another page

How to redirect to another page with passing selected row data as a prop?
I'm using material-table and I want to pass the selected rows data to another page after clicking the "Export" button, so I can use that data to create some kind of report in another page.
I think I should use history.push() method but it's not working in the onClick method. Can someone please give me any hint?
import React from 'react'
import MaterialTable from 'material-table';
class LeadTable extends React.Component{
constructor(props) {
super(props);
this.state = {
leads : [],
};
}
componentDidMount() {
fetch('http://localhost:5000/api/Leads')
.then(res => res.json())
.then((data) => {
// console.log('Data: ', data[0])
this.setState({
leads: data[0]
})
})
.catch(console.log);
}
redirectToReport = () => {
const { history } = this.props;
history.push('report');
}
render(){
return (
<div style={{ maxWidth: '100%' , align: 'center'}}>
<MaterialTable
title="Reporting"
columns={[
...
]}
data = {this.state.leads}
options={{
selection: true,
filtering: true,
sorting: true
}}
actions = {[{
position: "toolbarOnSelect",
tooltip: 'Export the selected activities!',
icon: 'Export',
onClick: (event, rowData) => {
console.log("Row Data: " , rowData)
// rowData has all the selected row and I want to redirect to another page with passing those data.
}
}]}
/>
</div>
)}
}
export default LeadTable
This answer mainly addresses OP's code base which is using class components. If you are using function components you can use react-router hooks such as useHistory
Use withRouter HOC to enable LeadTable component access to history so you can push
const LeadTableWithRouter = withRouter(LeadTable);
Pass object to push function to pass row data
redirectToReport = (rowData) => {
const { history } = this.props;
history.push({
pathname: "/report", // re-route to this path
state: { name: rowData.name, surname: rowData.surname } // your row data
});
};
In your other component, use this.props.location.state.<data_name> to access the row data you've passed
class AnotherPage extends React.Component {
render() {
return (
<>
<p>{this.props.location.state.name}</p>
<p>{this.props.location.state.surname}</p>
<Link to="/">go back</Link>
</>
);
}
}

Edit an Item in a list

I am creating a to-do app using react js and semantic-ui-library.
Although Add and delete functions are working perfectly, I am stuck at moving on with edit function. Following are code snippets.
App.js
class App extends Component{
constructor(props){
super(props);
this.state={
item :'',
listItems:[
],
}
this.handleChangeItem = this.handleChangeItem.bind(this);
}
updateItem=(key,item)=>{
const newlistItems = [...this.state.listItems]
newlistItems.map(list=>{
if(list.key===key){
list.item = item;
}
}
)
this.setState({
listItems:newlistItems
})
}
ListView.js
<List.Content>
{
this.props.listItems.map((item, index) => <List.Header key={index} >
<span>
<input
size="50%"
id={index}
value={item}
onChange={(event)=>{this.props.updateItem(event.target.value,index)}}
/>
</List.Content>
I am implementing the edit function in parent component and calling it for onChange method in input field of the List View Component. I am unable to edit the value of the input field in the view component.
Can anybody help me to sort this out?
The problem is with your updateItem function. You can use setState with a callback to access the previous state of your state.
Try the following:
updateItem = (key, item) => {
this.setState(prevState => {
...prevState,
listItems: prevState.listItems.map(list => {
if(list.key === key){
list.item = item;
}
return list;
})
})
}
And maybe on your onChange event you want to pass item instead of index:
onChange={ event => { this.props.updateItem(event.target.value, item) } }
I hope this helps!

React integrate component through functions ( HOC )

I am building an admin template using reactJs and one of the components that make up the entire page looks like this.
class UserManagement extends React.Component {
state = {
showDeleteModal: false
};
_toggleDeleteModal = () => {
this.setState(prevState => ({
showDeleteModal: !prevState.showDeleteModal
}));
};
onDeleteRow = () => {
console.log("delete");
};
render() {
return (
<div>
{this.state.showDeleteModal && (
<DeleteModal
title="Delete Data ?"
description="Are you sure you want to delete this data from the system ?"
onDeleteAction={this.onDeleteRow}
onToggleModal={this._toggleDeleteModal}
/>
)}
</div>
);
}
}
DeleteModal is basically a modal that pops up and displays a bunch of options to the user based on which the user selects an option, this is one of the many modals that are contained in this UserManagement component. As you can see I need to write out the DeleteModal code in the render function, doing this for the other modals causes excess code on this page that can probably be extracted out somehow.
In the end I would like to be able to do something like th
I didn't get your question clearly but am hoping you are asking how you could extract out the DeleteModal component. That being said, here is my thought;
class UserManagement extends React.Component {
state = {
showDeleteModal: false
};
_toggleDeleteModal = () => {
this.setState(prevState => ({
showDeleteModal: !prevState.showDeleteModal
}));
};
onDeleteRow = () => {
console.log("delete");
};
renderDeleteModal = () => (
<DeleteModal
title={"Delete Data ?"}
description={
"Are you sure you want to delete this data from the system ?"
}
onDeleteAction={this.onDeleteRow}
onToggleModal={this._toggleDeleteModal}
/>
);
render() {
return (
<React.Fragment>
{this.state.showDeleteModal && this.renderDeleteModal}
</React.Fragment>
);
}
}
I'm making the assumption that all the modals that you have are having similar structure, and since at any point of time only one modal will be shown to the user
you can create on reusable modal that has the following fields:
Title
Description
Action button
Cancel button
You can try creating something like this:
class UserManagement extends React.Component {
constructor(props) {
this.state = {
showModal: false,
modalTitle: "",
modalDescription: "",
modalAction: null
}
}
showDeleteModal() {
this.setState(prevState => ({
modalTitle: "Delete Data ?",
modalDescription: "Are you sure you want to delete this data from the system ?",
modalAction: this.onDeleteRow
}), this._toggleDeleteModal)
}
_toggleDeleteModal = () => {
this.setState(prevState => ({
showModal: !prevState.showModal
}))
};
onDeleteRow = () => {
console.log("delete");
};
render() {
return (
<div>
{this.state.showModal && (
<Modal
data={this.state.modal}
onToggleModal={this._toggleModal}
/>
)}
</div>
);
}
}
You can have one specific function for each of your use case (like Delete) which sets that title, description etc.
You can further move all the code that I've shown to a HOC and import it in your UserManagement component, if you think they will perform static operations with no input requirement from UserManagement component.

react-select can load async data

I'm trying to build a select component using react-select plugin.
In the process of implementing this project, I have some kind of tricky problem with that. Check out my source code here: https://codesandbox.io/s/j148r99695
The problem that I have is I want to fetch all genresList data from the server and mapping them to select component. But somehow or I do wrong something, It's not working. Please see source code above to help me.
I fetch data from Movies component. Its work well and I pass a props to FormFilter component: <FormFilter genresList={this.state.genres} />. And in the FormFilter component, I check this.props.genresList, it's available. But when I'm trying to assign it to FormFilter state and console.log("state", this.state.genres); that. It's empty. Anyone can tell me why?
Default react-select using value and label to display data to select component. But you know some cases we have to custom that. I try it out by using map to transform to other arrays. But It's the best way? How can I custom valueKey and labelKey.
I'm using react-select beta version2.
UPDATE: I was fixed my project. Please check out the link below. Somehow it's not working. I was commend inside source code.
https://codesandbox.io/s/moym59w39p
So to make it works I have changed the FormFilter.js implementation:
import React, { Component } from "react";
import * as Animated from "react-select/lib/animated";
import AsyncSelect from "react-select/lib/Async";
class FormFilter extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: "",
selectedOption: "",
genres: []
};
}
selectGenreHandleChange = newValue => {
const inputValue = newValue.replace(/\W/g, "");
this.setState({ inputValue });
console.log(inputValue);
};
componentDidMount() {
this.genresOption();
}
filterGenres = inputValue => {
const genres = this.genresOption();
//HERE - return the filter
return genres.filter(genre =>
genre.label.toLowerCase().includes(inputValue.toLowerCase())
);
};
promiseOptions = inputValue => {
return new Promise(resolve => { // HERE - you have to return the promise
setTimeout(() => {
resolve(this.filterGenres(inputValue));
}, 1000);
});
};
genresOption() {
const options = [];
const genres = this.props.genresList.genres; //HERE - array is genres in genresList
if (genres && genres instanceof Array) {
genres.map(genre => options.push({ value: genre.id, label: genre.name}));
}
return options;
}
render() {
const { inputValue } = this.state;
if (this.state.genres) console.log("state", this.state.genres);
if (this.props.genresList)
console.log("Movies props", this.props.genresList);
return (
<div className="filter_form">
<span className="search_element full">
<label htmlFor="genres">Genres</label>
<AsyncSelect
className="select genres"
classNamePrefix="tmdb_select"
isMulti
isSearchable="true"
isClearable="true"
cacheOptions
components={Animated}
value={inputValue}
defaultOptions
onInputChange={this.selectGenreHandleChange}
loadOptions={this.promiseOptions}
/>
</span>
</div>
);
}
}
export default FormFilter;
I have write a comment "HERE - something" to let you know what I changed. There are not big problems :)
I did some changed in your FIDDLE and it's works for me
Something like
import React, {Component} from "react";
import { render } from 'react-dom';
import Movies from './Movies';
import "./styles.css";
class App extends Component {
render() {
return (
<div className="App">
<Movies />
</div>
);
}
}
let a = document.getElementById("root");
render(<App />, a);

Display some text in react depending on the switch case

I have a dropdown populated from a Web Service, what I want is to display some text according to the selection made. For example the first option in the Dropdown is Buy n and Save m so in a p tag I want to display Buy 2 and Save $1.5 I know this is work for a switch and the position of the array is going to be my "CASE" in order to know what to display or not but I'm new to react and also in programming so I need help..
import React from 'react';
import DropDownMenu from 'material-ui/DropDownMenu';
import MenuItem from 'material-ui/MenuItem';
import cr from '../styles/general.css';
export default class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
OfferTypeData: [],
OfferTypeState: '',
};
this.handleChange = this.handleChange.bind(this);
this.renderOfferTypeOptions = this.renderOfferTypeOptions.bind(this);
}
componentDidMount() {
const offerTypeWS = 'http://localhost:8080/services/OfferType/getAll';
fetch(offerTypeWS)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
OfferTypeData: findResponse
});
});
}
handleChange(event, index, value) {this.setState({value});}
handleChangeDiscountType(event, index, value) {
this.setState({ OfferTypeState: (value) });
}
renderOfferTypeOptions() {
return this.state.OfferTypeData.map((dt, i) => {
return (
<MenuItem
key={i}
value={dt.offerTypeDesc}
primaryText={dt.offerTypeDesc} />
);
});
}
render() {
return (
<div className={cr.container}>
<div className={cr.rows}>
<div>
<DropDownMenu
value={this.state.OfferTypeState}
onChange={this.handleChangeDiscountType}>
<MenuItem value={''} primaryText={'Select Offer Type'} />
{this.renderOfferTypeOptions()}
</DropDownMenu>
<br/>
<p>{DISPLAY SOME TEXT HERE}</p>
</div>
</div>
</div>
);
}
}
Thanks in advance!
Regards.
Create a component which passes a callback to the dropdown, this callback will update the state of the container which will in turn set the props of the display. This is very common in React and is the basis of how the compositional pattern works. If you need to share data between two components just put them in a container and lift the state to the parent component. These components are usually called containers and there is a bunch of documentation on it.
This is a good starting point: https://reactjs.org/docs/lifting-state-up.html
A rough layout would be something like this.
class Container extends React.Component {
constructor(props) {
super(props);
// Don't forget to bind the handler to the correct context
this.changeText = this.changeText.bind(this);
}
changeText(text) {
this.setState({text: text});
}
render() {
return (
<DropDown callback={this.changeText} />
<Display text={this.state.text} />
)
}
}
Display component...
const Display = (props) => (
<p>{this.props.text}</p>
)

Categories

Resources