Avoid navigation in react.js on button click - reactjs

I am very new to react.js and trying to create a react component to render a json response from a REST API created in Python-Flask on button click.
Everything works fine but I am being navigated to the same page again and output which is a table does not persist.
We can see the console shows navigation back to the same page, which resets the component's state.
Snapshot of the console output shows the behavior
My component code:
var cols = [
{ key: 'id', label: 'Id' },
{ key: 'owner', label: 'Owner' },
{ key: 'path', label: 'Path' },
{ key: 'description', label: 'Description' }
];
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.state = {items: [], searchString: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({searchString: event.target.value});
console.log(this.state.items);
}
handleSubmit(event) {
// alert('A name was submitted: ' + this.state.searchString);
// event.preventDefault();
// this.getMoviesFromApiAsync();
console.log(this.state.searchString);
this.getData();
}
getData() {
// Todo: Append the searchstring to the URI
fetch("http://localhost:5000/search")
.then(response => response.json())
.then(json => {
console.log("Inside request: ");
console.log(json.Data);
this.setState({
items: json.Data
});
console.log("after copy to state");
console.log(this.state.items);
});
}
generateRows() {
var cols = this.props.cols, // [{key, label}]
data = this.state.items;
console.log("Inside functions");
console.log(data);
// console.log(data);
return data.map(function(item) {
// handle the column data within each row
var cells = cols.map(function(colData) {
// colData.key might be "firstName"
return <td key={colData.key}> {item[colData.key]} </td>;
});
return <tr key={item.id}> {cells} </tr>;
});
}
generateHeaders() {
var cols = this.props.cols; // [{key, label}]
// generate our header (th) cell components
return cols.map(function(colData) {
return <th key={colData.key}> {colData.label} </th>;
});
}
render() {
var headerComponents = this.generateHeaders(),
rowComponents = this.generateRows();
return (
<div>
<form onSubmit={this.handleSubmit.bind(this)}>
<input type="text" value={this.state.searchString} onChange={this.handleChange.bind(this)} />
<input type="submit" value="Search" />
</form>
<br />
<div class="table-responsive">
<table class="table">
<thead> {headerComponents} </thead>
<tbody> {rowComponents} </tbody>
</table>
</div>
</div>
);
}
}
module.exports = SearchForm;
const main = document.getElementById('main');
ReactDOM.render(<SearchForm cols={cols}/>, main);

Formatting this as an answer for future reference. When using a form, the event handler for form submission needs to have
event.preventDefault()
in order to keep the default form submission and redirect from happening.
Reference

Related

How to change let value onClick in React

My default value is null (let activestatus = "";), but I want it to change on click to be:
let activestatus = "?IsActive=0";
I am getting value on click (as seen in console), but the value is not passed in "let activestatus".
class App extends Component {
state = {
reservations: [],
};
componentWillMount() {
let activestatus = "";
axios
.get("https://localhost:44307/api/GetReservations/" + `${activestatus}`)
.then((response) => {
this.setState({
reservations: response.data,
});
});
}
showActive = (e) => {
e.preventDefault();
console.log(e.target.value);
this.activestatus = e.target.value;
};
render() {
let reservations = this.state.reservations.map((reservation) => {
return (
<tr>
<td>{reservation.Id}</td>
</tr>
);
});
return (
<div className="App container">
<Button
class="activity-button"
value={"?IsActive=0"}
id="active"
onClick={this.showActive}
>
Can you try to have activeStatus as part of your state? Also if you want to refresh the data from the api based on this field, then should probably use componentDidUpdate that runs on state changes.
class App extends Component {
state = {
reservations: [],
activestatus: ""
};
componentWillMount() {
axios
.get("https://localhost:44307/api/GetReservations/" + `${activestatus}`)
.then((response) => {
this.setState({
reservations: response.data,
});
});
}
showActive = (e) => {
e.preventDefault();
console.log(e.target.value);
this.setState({ activestatus: e.target.value });
};
render() {
let reservations = this.state.reservations.map((reservation) => {
return (
<tr>
<td>{reservation.Id}</td>
</tr>
);
});
return (
<div className="App container">
<Button
class="activity-button"
value={"?IsActive=0"}
id="active"
onClick={this.showActive}
>`
Thanks guys, both were helpful.
Solution:
class App extends Component {
state = {
reservations: [],
activestatus: "",
};
componentDidUpdate() {
axios
.get(
"https://localhost:44307/api/GetReservations/" +
`${this.state.activestatus}`
)
.then((response) => {
this.setState({
reservations: response.data,
});
});
}
}
showActive = (e) => {
e.preventDefault();
console.log(e.target.value);
this.setState({ activestatus: e.target.value });
};
render() {
let reservations = this.state.reservations.map((reservation) => {
return (
<tr>
<td>{reservation.Id}</td>
</tr>
);
});
return (
<div className="App container">
<Button
class="activity-button"
value={"?IsActive=0"}
id="active"
onClick={this.showActive}
>`

How can I pass my state to this class component in ReactJs/.Net?

I followed a tutorial to make an Asp.Net Core MVC app with a ReactJs front end (https://reactjs.net/tutorials/aspnetcore.html). I've been adding additional functionality to the project after completing the tutorial to see what else I can do with it.
My <AddColourForm> component assembles a <Colour> object and posts it off via an XmlHttpRequest to my API controller which in turn persists it to local storage. The submitUrl for the controller is passed in through the props. This works.
I've since tried to add the <SoftDeleteColour> component to each colourNode rendered in the <ColourList> which I intend to behave in more-or-less the same manner as the <AddColourForm> component. Each colourNode rendered in the <ColourList> has it's own delete button and I want the <SoftDeleteColour> component to take the colour.id from the selected colour and pass it to the softDelete action on the API controller so that can be handled in turn (it'll find the colour by id and append a DateDeleted to it, the API will then ignore any colours where DateDeleted != null) and the <SoftDeleteColour> component can then call loadColoursFromServer() to bring back the refreshed list from the storage. I want <SoftDeleteColour> to receive the softDeleteUrl from props in the same way that the add form does.
When I run the project in debug the softDeleteUrl is coming in as undefined and when I inspect the props in the browser it doesn't contain the softDeleteUrl. Also the "colour" is undefined so I feel like my <SoftDeleteColour> component isn't receiving the props or state. I'm new to React and struggling conceptually with props/state binding a little bit so I suspect this is the source of my problem.
How can I pass the softDeleteUrl and the properties of the colour from the <ColourList> that I am selecting for deletion to the <SoftDeleteColour> component? Do I need to call something like <SoftDeleteColour HandleDeletion=this.HandleDeletion.bind(this) /> or something?
class ColourDisplay extends React.Component {
constructor(props) {
super(props);
this.state = { data: [] };
this.handleColourSubmit = this.handleColourSubmit.bind(this);
}
loadColoursFromServer() {
const xhr = new XMLHttpRequest();
xhr.open('get', this.props.url, true);
xhr.onload = () => {
const data = JSON.parse(xhr.responseText);
this.setState({ data: data });
};
xhr.send();
}
handleColourSubmit(colour) {
const data = new FormData();
data.append('name', colour.name);
data.append('brand', colour.brand);
data.append('expiry', colour.expiry);
data.append('serialNumber', colour.serialNumber);
const xhr = new XMLHttpRequest();
xhr.open('post', this.props.submitUrl, true);
xhr.onload = () => this.loadColoursFromServer();
xhr.send(data);
}
componentDidMount() {
this.loadColoursFromServer();
}
render() {
return (
<div className="colourDisplay">
<h1>Colours</h1>
<ColourList data={this.state.data}/>
<AddColourForm onColourSubmit={this.handleColourSubmit}/>
</div>
);
}
}
class ColourList extends React.Component {
render() {
const colourNodes = this.props.data.map(colour => (
<Colour name={colour.name} key={colour.id}>
<div>Brand: {colour.brand}</div>
<div>Exp: {colour.expiry}</div>
<div>Serial #: {colour.serialNumber}</div>
<div>Date Added: {colour.dateAdded}</div>
<SoftDeleteColour />
</Colour>
));
return <div className="colourList">{colourNodes}</div>;
}
}
class SoftDeleteColour extends React.Component {
constructor(props) {
super(props)
this.state = {
colour: this.props.colour
};
}
HandleDeletion(colour) {
var xhr = new XMLHttpRequest();
var url = this.props.softDeleteUrl + colour.id;
xhr.open('DELETE', url, true);
xhr.onreadystatechange = () => {
if (xhr.status == 204) {
this.loadColoursFromServer();
}
}
xhr.send();
}
render() {
return (
<button onClick={() => { this.HandleDeletion(this.state.colour); }}>Delete</button>
)
}
}
class AddColourForm extends React.Component {
constructor(props) {
super(props);
this.state = { name: '', brand: '', expiry: '', serialNumber: '' };
this.handleNameChange = this.handleNameChange.bind(this);
this.handleBrandChange = this.handleBrandChange.bind(this);
this.handleExpiryChange = this.handleExpiryChange.bind(this);
this.handleSerialNumberChange = this.handleSerialNumberChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleNameChange(e) {
this.setState({ name: e.target.value });
}
handleBrandChange(e) {
this.setState({ brand: e.target.value });
}
handleExpiryChange(e) {
this.setState({ expiry: e.target.value });
}
handleSerialNumberChange(e) {
this.setState({ serialNumber: e.target.value })
}
handleSubmit(e) {
e.preventDefault();
const name = this.state.name.trim();
const brand = this.state.brand.trim();
const expiry = this.state.expiry.trim();
const serialNumber = this.state.serialNumber.trim();
if (!name || !brand || !expiry || !serialNumber) {
return;
}
this.props.onColourSubmit({
name: name,
brand: brand,
expiry: expiry,
serialNumber: serialNumber
})
this.setState({
name: '',
brand: '',
expiry: '',
serialNumber: ''
});
}
render() {
return (
<form className="addColourForm" onSubmit={this.handleSubmit}>
<h2>Add a colour to your list</h2>
<div>
<input
type="text"
placeholder="Colour"
value={this.state.name}
onChange={this.handleNameChange}
/>
</div>
<div>
<input
type="text"
placeholder="Brand"
value={this.state.brand}
onChange={this.handleBrandChange}
/>
</div>
<div>
<input
type="text"
placeholder="Expiry MM/YY"
value={this.state.expiry}
onChange={this.handleExpiryChange}
/>
</div>
<div>
<input
type="text"
placeholder="Serial #"
value={this.state.serialNumber}
onChange={this.handleSerialNumberChange}
/>
</div>
<input type="submit" value="Post" />
</form>
);
}
}
class Colour extends React.Component {
render() {
return (
<div className="colour">
<h2 className="colourName">{this.props.name}</h2>
{this.props.children}
</div>
);
}
}
ReactDOM.render(
<ColourDisplay
url="/colours"
submitUrl="/colours/new"
softDeleteUrl="/colours/softDelete"
/>,
document.getElementById('content')
);

React To Do List, How to add another item to a localStorage key array

I've managed to work out how to give the note key in localStorage a value from the addNote page but I can't seem to work out how to make it so that it creates a new value under the note key and not just re-assigning the note value. I'm believe having either a for loop which will loop to the next array index or if statement would fix this issue and allow for the adding of items however I'm just unsure where to place it. Any help in working this issue out would be greatly appreciated :)
Download React project files here: https://drive.google.com/drive/folders/1_P85WUmyY9QcZu14-Ib8IxAU3e4UtZCp
Below is the code I'm using to get a input value and add it to the note key in a file called storage.js (located in src/services/storage.js)
class AddNote extends Component {
constructor(props) {
super(props)
this.state = {
//title: [],
//content: []
items: []
}
let note = getLocalItem(keys.note);
if(!note) {
note = [];
}
this.addNote = this.addNote.bind(this);
}
addNote(event) {
console.log("Working")
if( this.theTitle.value !== "" ) {
var newItem = {
title: this.theTitle.value,
content: this.theContent.value
};
}
this.setState((prevState) => {
return {
items: prevState.items.concat(newItem)
};
});
const form = {
title: this.state.title,
content: this.state.content
}
setLocalItem('note', this.theTitle.value + " - " + this.theContent.value);
You just wan't to add the next content field 'note' to the existing value in the localStorage... the following code reads the localStorage value on componentDidMount; and then on each click of the button, updates (instead of replace) the localStorage array...
relevant component:
class App extends Component {
constructor(props) {
super(props);
this.state = {
inputs: [],
lastInput: ''
};
this.click = this.click.bind(this);
}
componentDidMount() {
this.getExistingArray();
}
getExistingArray() {
if (localStorage.getItem('inputs')) {
var storedInputs = localStorage.getItem('inputs');
this.setState({ inputs: storedInputs }, function () { console.log("from localStorage We got:", this.state.inputs); });
}
}
click() {
var newInput = [...this.state.inputs, this.state.lastInput];
localStorage.setItem('inputs', newInput);
this.getExistingArray();
}
recordInput(e) {
this.setState({ lastInput: e.target.value });
}
render() {
return (
<div>
<input type='text' onChange={this.recordInput.bind(this)} />
<button onClick={this.click}>Click to update the Array</button>
<br /> Array: {this.state.inputs}
</div>
);
}
}
complete working stackblitz
UPDATE: following function updated in light of questioner's comment
click() {
var newInput = [...this.state.inputs, this.state.lastInput];
localStorage.setItem('inputs', newInput);
this.getExistingArray();
var newInputTag = 'inputs' + this.state.inputs.length;
localStorage.setItem(newInputTag, this.state.lastInput);
}
UPDATE2:
All local storage objects retrieved and printed on the page
class App extends Component {
constructor(props) {
super(props);
this.state = {
inputs: [],
lastInput: '',
localStoragePairs: []
};
this.click = this.click.bind(this);
}
componentDidMount() {
this.getExistingArray();
}
getExistingArray() {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
var value = localStorage.getItem(key);
var updatedLocalStoragePairs = this.state.localStoragePairs;
updatedLocalStoragePairs.push({ 'keyName': key, 'valueName': value });
this.setState({ localStoragePairs: updatedLocalStoragePairs });
}
console.log("complete localStoragePairs:", this.state.localStoragePairs);
if (localStorage.getItem('inputs')) {
var storedInputs = localStorage.getItem('inputs');
this.setState({ inputs: storedInputs }, function () { console.log("from localStorage We got:", this.state.inputs); });
}
}
click() {
var newInput = [...this.state.inputs, this.state.lastInput];
localStorage.setItem('inputs', newInput);
this.getExistingArray();
var newInputTag = 'inputs' + this.state.inputs.length;
localStorage.setItem(newInputTag, this.state.lastInput);
}
recordInput(e) {
this.setState({ lastInput: e.target.value });
}
render() {
var LocalSotrageContent = this.state.localStoragePairs.map((value, index) => {
return <tr key={index}> <td>{value.keyName}</td> <td>{value.valueName}</td> </tr>
});
return (
<div>
<input type='text' onChange={this.recordInput.bind(this)} />
<button onClick={this.click}>Click to update the Array</button>
<table>
<thead>
<tr>
<th>All Local Storage objects by Name</th>
<th>All Local Storage objects by Value</th>
</tr>
</thead>
<tbody>
{LocalSotrageContent}
</tbody>
</table>
<br />
</div>
);
}
}

React Redux showing data in table from API

Currently, my application showing initialState data in the table and those data are hardcoded. I want to show my API fetched data in the table.
this is my postReducer.js file:
var initialState = {
employees: [
{ id: 1, name: 'jhon', age: '23'},
{ id: 2, name: 'doe', age: '24'}
]
};
var postReducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD_POST':
return {
...state,
employees: [...state.employees, action.data],
};
case 'EDIT_POST':
return {
...state,
employees: state.employees.map(emp => emp.id === action.data.id ? action.data : emp)
};
case 'DELETE_POST':
console.log(action.data.id)
return {
...state,
employees: [...state.employees.filter((post)=>post.id !== action.data.id)],
};
default:
return state;
}
};
export default postReducer;
and this is my table.js file
import React, {Fragment} from "react";
import { connect } from "react-redux";
class Table extends React.Component {
onEdit = (item) => { //Use arrow function to bind `this`
this.props.selectedData(item);
}
onDelete = (id) => {
const data = {
id,
}
this.props.dispatch({ type: 'DELETE_POST', data });
}
render() {
return (
<Fragment>
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Age</th>
<th scope="col">Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{this.props.employees.map((item, index) => (
<tr key={index}>
<td>{item.name}</td>
<td>{item.age}</td>
<td>
<button
type="button"
onClick={() => this.onEdit(item)}>EDIT
</button>
<button
onClick={ () => this.onDelete(item.id) }>DELETE
</button>
</td>
</tr>
))}
</tbody>
</Fragment>
);
}
}
const mapStateToProps = (state) => ({ employees: state.employees });
export default connect(mapStateToProps)(Table);
and this my form.js file
import React, { Fragment } from "react"
import { connect } from 'react-redux'
const axios = require('axios');
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
id: this.props.selectedData.id,
name: this.props.selectedData.name,
age: this.props.selectedData.age,
};
this.onHandleChange = this.onHandleChange.bind(this);
this.submit = this.submit.bind(this);
}
submit(event) {
const data = {
name: this.state.name,
age: this.state.age,
email: this.state.email
};
if (this.props.isEdit) {
data.id = this.props.selectedData.id;
console.log('edit', data);
this.props.dispatch({ type: 'EDIT_POST', data })
} else {
// generate id here for new emplyoee
this.props.dispatch({ type: 'ADD_POST', data })
}
}
onHandleChange(event) {
this.setState({
[event.target.name]: event.target.value
});
}
componentDidUpdate(prevProps) {
if (prevProps.selectedData.age !== this.props.selectedData.age) { //Check on email, because email is unique
this.setState({ name: this.props.selectedData.name, age: this.props.selectedData.age })
}
}
render() {
return (
<form>
<div className="form-group">
<input onChange={(event) => this.onHandleChange(event)} value={this.state.name} name="name" type="text" />
</div>
<div className="form-group">
<input onChange={(event) => this.onHandleChange(event)} value={this.state.age} name="age" type="number" />
</div>
<button onClick={(event) => this.submit(event)} type="button">
{this.props.isEdit ? 'Update' : 'SAVE'}
</button>
</form>
);
}
}
export default connect(null)(Form);
I think i need to work on table.js file to implement, i tried with componentDidmount but i failed to implement is.
I am using Axios for http request
this is the request snippet with api:
axios.get('http://127.0.0.1:8000/api/v1/employee/')
.then(function (response) {
// handle success
})
.catch(function (error) {
// handle error
})
.finally(function () {
});
I am not getting how to successfully implement this like when i visit the page, i should see the table with data that come from api endpoint.
Can anyone help me regarding this?
In your Table component, you can make use of componentDidMount for your API call,
componentDidMount(){
axios.get('http://127.0.0.1:8000/api/v1/employee/')
.then((response) => { //Use arrow function to auto bind `this`
// handle success
this.props.dispatch({ type: 'ADD_POST', response.data }) //considering response.data is the correct array
})
.catch(function (error) {
// handle error
})
.finally(function () {
});
}

React - how can I get updated data to show in my table?

I need to be able to update the values in my table rows and then have those new values show in the cells. How would I go about doing this?
Here is the code I am currently working with:
Main Table Component
import React from 'react';
import TableWithDataHeader from './TableWithDataHeader.jsx';
import TableWithDataBody from './TableWithDataBody.jsx';
import TableWithDataRowForm from './TableWithDataRowForm.jsx';
import {updateRowHistory} from '../../actions/DALIActions';
import AppStore from '../../stores/AppStore';
export default class TableWithData extends React.Component {
state = {rows: [], isEditing: false, input: null};
updateState = () => {
let rows = this.state.rows;
rows.shift();
rows.push({id: AppStore.getRowId(), cells: AppStore.getUpdatedCells()});
this.setState({rows});
console.log(rows);
};
componentDidMount() {
let rows = this.state.rows;
rows.push({id: AppStore.getRowId(), cells: AppStore.getCells().historycells});
this.setState({rows});
console.log(rows);
AppStore.addChangeListener(this.updateState);
}
handleEdit = (row) => {
this.setState({isEditing: true});
};
handleInputChange = (newCellValuesArray) => {
let input = this.state.input;
input = newCellValuesArray;
this.setState({input});
};
editStop = (row) => {
this.setState({isEditing: false});
};
handleSubmit = (access_token, row_id) => {
let newCellValuesArray = this.state.input;
updateRowHistory(access_token, row_id, newCellValuesArray);
this.setState({isEditing: false});
};
componentWillUnmount() {
AppStore.removeChangeListener(this.updateState);
}
render() {
let {rows, isEditing, input} = this.state;
console.log(rows);
console.log(rows.map(row => {
return row.cells;
}));
return (
<div>
<div className="row">
<table className="table table-striped">
<thead>
<TableWithDataHeader />
</thead>
<tbody>
{rows.map(row => this.state.isEditing ?
<TableWithDataRowForm key={row.id} cells={row.cells} editStop={this.editStop.bind(null, row)} handleSubmit={this.handleSubmit.bind(this)} handleInputChange={this.handleInputChange.bind(this)} /> :
<TableWithDataBody key={row.id} cells={row.cells} handleEdit={this.handleEdit.bind(null, row)} />
)}
</tbody>
</table>
</div>
</div>
);
}
}
Edit Row Component
import React from 'react';
import AppStore from '../../stores/AppStore';
export default class TableWithDataRowForm extends React.Component {
state = {cells: this.props.cells, newCellValues: []};
onChange(e) {
let newCellValues = this.state.newCellValues;
newCellValues[e.target.id] = e.target.value;
this.setState({newCellValues});
console.log(newCellValues);
let newCellValuesArray = [];
for (let key in newCellValues) {
if (newCellValues.hasOwnProperty(key)) {
newCellValuesArray.push({contents: newCellValues[key]});
}
}
console.log(newCellValuesArray);
this.props.handleInputChange(newCellValuesArray);
}
editStop() {
this.props.editStop();
}
handleSubmit(e) {
e.preventDefault();
let access_token = AppStore.getToken();
let row_id = AppStore.getRowId();
this.props.handleSubmit(access_token, row_id);
}
render() {
let {cells, newCellValues} = this.state;
return (
<tr>
{cells.map(cell => {
return <td key={cell.id} className="text-center"><input type="text" className="form-control" id={cell.id} defaultValue={cell.contents} onChange={this.onChange.bind(this)} /></td>
})}
<td>
<button className="btn btn-default"><i className="fa fa-ban" onClick={this.editStop.bind(this)}></i>Cancel</button>
<button className="btn btn-success"><i className="fa fa-cloud" onClick={this.handleSubmit.bind(this)}></i>Save</button>
</td>
</tr>
);
}
}
It's it bit mangled at the moment, but I think that you can get the general idea of what I am attempting! So, I can get the table to initially render with data values from my store and I can successfully edit them to different values. However, I would like it so that when I click my save button the new values show. I am using React with flux to build this.
Answers with examples are always much appreciated
Thanks for your time
Your problem is that you have the state of our cells twice.
Once in your row and once in your table.
You should never do this but have the state only in the table and pass them as prop and access them as prop. Only the temporary edited vakue should be saved as an extra state.
You can get the prop changes via componentWillReceiveProps.
Here an stripped down example:
var Row = React.createClass({
getInitialState: function() {
return {
tempValue: this.props.value
}
},
componentWillReceiveProps: function(nextProps) {
//here u might want to check if u are currently editing but u get the idea -- maybe u want to reset it to the current prop on some cancelEdit method instead
this.setState({
tempValue: nextProps.value
});
},
render: function() {
return <div><input type="text" value={this.state.tempValue} onChange={this.onChange} /></div>;
},
onChange: function(e) {
this.setState({
tempValue: e.target.value
});
}
});
var Hello = React.createClass({
getInitialState: function() {
return {
value: 'someServerState'
}
},
render: function() {
return (
<div>
<Row value={this.state.value} />
<button onClick={this.reloadFromServer} >reload from Server</button>
</div>
);
},
//this will be triggered by some of ur events - i added a button
reloadFromServer: function() {
this.setState({
value: 'someServerState changed somehow'
});
}
});
see: https://jsfiddle.net/69z2wepo/34292/

Resources