Delete remove specific collection item - reactjs

Here's my current code :
/* ************************************* */
/* ******** IMPORTS ******** */
/* ************************************* */
import React, { Component } from 'react';
import UUID from 'node-uuid';
import { Card, CardBlock, Button, InputGroup, Input } from 'reactstrap';
import ProviderInfos from '../ProviderInfos/ProviderInfos';
/* ************************************* */
/* ******** VARIABLES ******** */
/* ************************************* */
/* ************************************* */
/* ******** COMPONENT ******** */
/* ************************************* */
export default class PretzelStandComponent extends Component {
constructor(props) {
super(props);
this.state = {
inputPretzel: [],
inputCurry: [],
inputWurst: []
};
this.incrementPretzel = this.incrementPretzel.bind(this);
this.incrementCurry = this.incrementCurry.bind(this);
this.incrementWurst = this.incrementWurst.bind(this);
this.decrementPretzel = this.decrementPretzel.bind(this);
this.decrementCurry = this.decrementCurry.bind(this);
this.decrementWurst = this.decrementWurst.bind(this);
}
componentDidMount() {
this.incrementPretzel();
this.incrementCurry();
this.incrementWurst();
}
incrementPretzel() {
const uuid = require('uuid/v1');
uuid();
const inputPretzel = this.state.inputPretzel;
this.setState({
inputPretzel: inputPretzel.concat(<InputGroup>
<Input placeholder="Pretzel" key={uuid} /><ProviderInfos /></InputGroup>),
});
}
incrementCurry() {
const uuid = require('uuid/v1');
uuid();
const inputCurry = this.state.inputCurry;
this.setState({
inputCurry: inputCurry.concat(<InputGroup>
<Input placeholder="Curry" key={uuid} /><ProviderInfos /></InputGroup>),
});
}
incrementWurst() {
const uuid = require('uuid/v1');
uuid();
const inputWurst = this.state.inputWurst;
this.setState({
inputWurst: inputWurst.concat(<InputGroup>
<Input placeholder="Wurst" key={uuid} /><ProviderInfos /></InputGroup>),
});
}
decrementPretzel() {
this.setState({
inputPretzel: this.state.inputPretzel.splice(this.state.inputPretzel, this.state.inputPretzel.length - 1),
});
}
decrementCurry() {
this.setState({
inputCurry: this.state.inputCurry.splice(this.state.inputCurry, this.state.inputCurry.length - 1),
});
}
decrementWurst() {
this.setState({
inputWurst: this.state.inputWurst.splice(this.state.inputWurst, this.state.inputWurst.length - 1),
});
}
render() {
return (
<Card>
<CardBlock className="main-table">
<fieldset>
<legend>Pretzels</legend>
{this.state.inputPretzel}
<button onClick={this.incrementPretzel}>Add a Pretzel</button>
<button onClick={this.decrementPretzel}>Remove a Pretzel</button>
</fieldset>
<fieldset>
<legend>Curry</legend>
{this.state.inputCurry}
<button onClick={this.incrementCurry}>Add Curry</button>
<button onClick={this.decrementCurry}>Remove Curry</button>
</fieldset>
<fieldset>
<legend>Wurst</legend>
{this.state.inputPretzel}
<button onClick={this.incrementPretzel}>Add Wurst</button>
<button onClick={this.decrementPretzel}>Remove Wurst</button>
</fieldset>
<Button color="secondary">Options</Button>{' '}
<Button id="btn">Exécuter</Button>
</CardBlock>
</Card>
);
}
}
As you can see I have three different elements and collections of these elements :
Pretzels
Currys
and Wursts
I can add them and remove the last one. but I'd like to remove each one.
In the Html code that I'm placing in the setState and adding to each collection I want to append a delete button or somehow have a delete button next to each line wich deletes the right line.

Update
I added some parts to keep track of input state and add a value to each item:
pretzelValue
curryValue
wurstValue
These are the value of the inputs, which are then passed into the increment functions. Also removed input from the FoodType component, if you want to be able to edit them it's a bit trickier.
Original
You can clean it up a bit by just using arrays of objects for the sets of food. Then using another component for the FoodType should make it much cleaner and give good performance for onClick. Each item has it's own uuid, so you can .filter on that to remove the item from the state.
The state and functions to add/remove could be more generic, but this is a decent start.
Something like this:
const uuid = require('uuid/v1');
export default class PretzelStandComponent extends Component {
state = {
pretzels: [],
curries: [],
wursts: [],
pretzelValue: '',
curryValue: '',
wurstValue: ''
}
componentDidMount() {
}
incrementPretzel = () => {
this.setState({
pretzels: this.state.pretzels.concat([{id: uuid(), value: this.state.pretzelValue}]),
pretzelValue: ''
});
}
incrementCurry = () => {
this.setState({
curries: this.state.curries.concat([{id: uuid(), value: this.state.curryValue}]),
curryValue: ''
});
}
incrementWurst = () => {
this.setState({
wursts: this.state.wursts.concat([{id: uuid(), value: this.state.wurstValue}]),
wurstValue: ''
});
}
decrementPretzel = (id) => {
this.setState({
pretzels: this.state.pretzels.filter((pretzel) => pretzel.id !== id)
});
}
decrementCurry = (id) => {
this.setState({
curries: this.state.curries.filter((curry) => curry.id !== id)
});
}
decrementWurst = (id) => {
this.setState({
wursts: this.state.wursts.filter((wurst) => wurst.id !== id)
});
}
onPretzelChange = (event) => {
this.setState({
pretzelValue: event.target.value
});
}
onCurryChange = (event) => {
this.setState({
curryValue: event.target.value
});
}
onWurstChange = (event) => {
this.setState({
wurstValue: event.target.value
});
}
render() {
const {pretzels, curries, wursts} = this.state;
return (
<Card>
<CardBlock className="main-table">
<fieldset>
<legend>Pretzels</legend>
{pretzels.map((pretzel) => (
<FoodType id={pretzel.id} placeholder="Pretzel" onRemove={this.decrementPretzel} value={pretzel.value} />
))}
<input onChange={this.onPretzelChange} value={this.state.pretzelValue} />
<button onClick={this.incrementPretzel}>Add a Pretzel</button>
</fieldset>
<fieldset>
<legend>Curry</legend>
{curries.map((curry) => (
<FoodType id={curry.id} placeholder="Curry" onRemove={this.decrementCurry} value={curry.value} />
))}
<input onChange={this.onCurryChange} value={this.state.curryValue} />
<button onClick={this.incrementCurry}>Add Curry</button>
</fieldset>
<fieldset>
<legend>Wurst</legend>
{wursts.map((wurst) => (
<FoodType id={wurst.id} placeholder="Wurst" onRemove={this.decrementWurst} value={wurst.value} />
))}
<input onChange={this.onWurstChange} value={this.state.wurstValue} />
<button onClick={this.incrementWurst}>Add Wurst</button>
</fieldset>
<Button color="secondary">Options</Button>{' '}
<Button id="btn">Exécuter</Button>
</CardBlock>
</Card>
);
}
}
FoodType component
class FoodType extends Component {
onRemove = () => {
this.props.onRemove(this.props.id);
}
render() {
const {placeholder, id, value} = this.props;
return (
<InputGroup>
<div key={id}>{value}</div>
<ProviderInfos />
<button onClick={this.onRemove}>X</button>
</InputGroup>
);
}
}
Also cleaned up the binds with the property initializer syntax.

Related

How to delete an element in list in react?

i am trying to do a simple toDo app with react. I couldnt do how to delete an element in list. Here my code; first state:
class AppForm extends Component {
constructor(props) {
super(props);
this.state = { items: [] , text:''};
this.onChangeHandler=this.onChangeHandler.bind(this)
this.submitHandler=this.submitHandler.bind(this)
}
//setting input value to the text in state
onChangeHandler = (e) => {
this.setState({
text: e.target.value
});
};
//pushing text item of the state to the items
submitHandler = (e) => {
e.preventDefault();
const arrayItem = {
text: this.state.text,
};
this.setState(state => ({
items: state.items.concat(arrayItem),
text: ''
}));
}
here the problem area. I also tried splice but couldnt.
deleteItem=(index)=>{
let todos= this.state.items.filter(todo => index !== todo.key)
this.setState({
items : todos
})
}
then rendering..
render() {
return (
<div>
<h1>toDo App</h1>
<form onSubmit={this.submitHandler}>
<label>Type the task you want to do!</label>
<input type="text" onChange={this.onChangeHandler} value={this.state.text}/>
</form>
<ul>
{this.state.items.map((item,index) =>{
return (
<li key={index}> {item.text}
<p onClick={this.deleteItem.bind(this,index)}> X </p>
</li>
)
})}
</ul>
</div>
);
}
}
export default AppForm;
Splice is the answer.
First, I create a copy of your state array. Then splice it using the index clicked. Then set setState with the spliced array.
deleteItem=(index)=>{
let todos= [...this.state.items]
todos.splice(index, 1)
this.setState({
items : todos
})
}
deleteItem = (index) => {
this.setState(({items}) => {
return {items: [...items.filter(todo => index !== todo.key)]};
})
}
First of all you're not setting the key anywhere when you are inserting in array. It is not at all recommended to use index as key in array. It should be unique.
const arrayItem = {
text: this.state.text,
id: uuid()
};
So I've added the uuid and compared with the id of the element.
codesandbox
uuid
// UNIQUE KEY GENERATOR
function uuidv4() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
export default uuidv4;
React component
import React, { Component } from "react";
import uuid from "./uuid";
import "./styles.css";
class App extends Component {
constructor(props) {
super(props);
this.state = { items: [], text: "" };
this.onChangeHandler = this.onChangeHandler.bind(this);
this.submitHandler = this.submitHandler.bind(this);
}
//setting input value to the text in state
onChangeHandler = (e) => {
this.setState({
text: e.target.value
});
};
//pushing text item of the state to the items
submitHandler = (e) => {
e.preventDefault();
const arrayItem = {
text: this.state.text,
id: uuid()
};
this.setState((state) => ({
items: state.items.concat(arrayItem),
text: ""
}));
};
deleteItem = (key) => {
let todos = this.state.items.filter((todo) => key !== todo.id);
this.setState({
items: todos
});
};
render() {
return (
<div>
<h1>toDo App</h1>
<form onSubmit={this.submitHandler}>
<label>Type the task you want to do!</label>
<input
type="text"
onChange={this.onChangeHandler}
value={this.state.text}
/>
</form>
<ul>
{this.state.items.map((item) => {
return (
<li key={item.id}>
{item.text}
<p onClick={() => this.deleteItem(item.id)}> X </p>
</li>
);
})}
</ul>
</div>
);
}
}
export default App;

optimistic ui updates - react

I imagine this is a basic in react but I'm not sure how to get it to work, basically when I delete, create or edit anything in my components I want the change to happen in realtime without refreshing the page, I've achieved it at some level with the search function but not entirely sure how to do with for the delete function for example:
Here is what I'm working with, how would I get this to work with my axios delete function?
Thanks
import { connect } from 'react-redux';
import { fetchTournaments } from '../actions/tournaments';
import Item from './Item';
import EditTournament from './EditTournament';
import axios from 'axios';
import '../styles/Item.css';
class SearchAndDisplay extends React.PureComponent {
componentDidMount() {
this.props.fetchTournaments();
}
state = {
searchCriteria: '',
isLoading: false
};
handleChange = event => {
this.setState({
searchCriteria: event.target.value
});
};
async handleDelete(id) {
const url = `http://localhost:4000/tournaments/`;
await axios
.delete(url + id)
.then(res => {
console.log(res.data);
})
.catch(err => {
console.log(err);
});
}
formatDate(date) {
let options = {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
hour12: false
};
let newDate = new Date(Date.parse(date));
let format = new Intl.DateTimeFormat('default', options).format(newDate);
return format;
}
handleChange = event => {
this.setState({ searchCriteria: event.target.value });
};
renderList() {
let tournmentsArray = this.props.tournaments;
const filterTournaments = tournmentsArray.filter(item =>
item.name.includes(this.state.searchCriteria)
);
if (filterTournaments === undefined || filterTournaments.length === 0) {
return (
<React.Fragment>
<div className="notFound">
Something went wrong.
<br />
<button
className="notFoundButton"
onClick={() => {
this.setState({ searchCriteria: '' });
}}
>
Retry
</button>
</div>
</React.Fragment>
);
} else {
return filterTournaments.map(item => (
<Item
key={item.name}
name={item.name}
organizer={item.organizer}
participants={Object.values(item.participants)}
game={item.game}
start={this.formatDate(item.startDate)}
>
<div className="buttonBar">
<EditTournament id={item.id} />
<button
className="button"
onClick={() => {
if (
window.confirm('Are you sure you want to delete this item?')
) {
this.handleDelete(item.id);
}
}}
>
Delete
</button>
</div>
</Item>
));
}
}
render() {
return (
<div className="container">
<input
onChange={this.handleChange}
className="input"
placeholder="Search..."
id="searchField"
value={this.state.searchCriteria}
/>
<div className="row">{this.renderList()}</div>
</div>
);
}
}
function mapStateToProps({ tournaments }) {
return {
tournaments: Object.values(tournaments).flat()
};
}
export default connect(mapStateToProps, {
fetchTournaments
})(SearchAndDisplay);
unlike delete the create and edit data is handled by redux like so:
Create tournament:
import { reduxForm, Field } from 'redux-form';
import '../styles/promptForms.css';
import '../styles/Header.css';
import { connect } from 'react-redux';
import { createTournaments } from '../actions/tournaments';
class CreateTournamentPromptFrom extends React.Component {
constructor(props) {
super(props);
this.state = {
showHide: false
};
}
createTournamentButton() {
return (
<div>
<button
className="genericButton"
onClick={() => this.setState({ showHide: true })}
>
CREATE TOURNAMENT
</button>
</div>
);
}
renderInput = ({ input, label }) => {
return (
<div>
<label>{label}</label>
<br />
<input className="promptInput" {...input} autoComplete="off" />
</div>
);
};
onSubmit = formValues => {
this.props.createTournaments(formValues);
};
render() {
const { showHide } = this.state;
return (
<React.Fragment>
<div className={`overlay ${showHide ? 'toggle' : ''} `} />
<div className={`promptBox ${showHide ? 'toggle' : ''} `}>
<h3>localhost:3000 says</h3>
<form onSubmit={this.props.handleSubmit(this.onSubmit)}>
<Field
name="name"
component={this.renderInput}
label="Enter Tournament:"
/>
<button className="okayButton">OK</button>
</form>
<button
className="cancelButton"
onClick={() => this.setState({ showHide: false })}
>
Cancel
</button>
</div>
{this.createTournamentButton()}
</React.Fragment>
);
}
}
const formWrapped = reduxForm({
form: 'promptForm'
})(CreateTournamentPromptFrom);
export default connect(null, { createTournaments })(formWrapped);
actions:
import {
FETCH_TOURNAMENTS,
FETCH_TOURNAMENT,
CREATE_TOURNAMENT,
EDIT_TOURNAMENT
} from './types';
import { API_TOURNAMENTS_URL } from '../constants/api';
import axios from 'axios';
export const fetchTournaments = () => async dispatch => {
const response = await axios.get(API_TOURNAMENTS_URL);
dispatch({
type: FETCH_TOURNAMENTS,
payload: response.data.flat()
});
};
export const fetchTournament = id => async dispatch => {
const response = await axios.get(`http://localhost:4000/tournaments/${id}`);
dispatch({ type: FETCH_TOURNAMENT, payload: response.data });
};
export const createTournaments = formValues => async dispatch => {
const response = await axios.post(API_TOURNAMENTS_URL, {
...formValues
});
dispatch({ type: CREATE_TOURNAMENT, payload: response.data });
};
export const editTournaments = (id, formValues) => async dispatch => {
const response = await axios.patch(
`http://localhost:4000/tournaments/${id}`,
formValues
);
dispatch({ type: EDIT_TOURNAMENT, payload: response.data });
};
reducers:
import _ from 'lodash';
import {
FETCH_TOURNAMENT,
CREATE_TOURNAMENT,
FETCH_TOURNAMENTS,
EDIT_TOURNAMENT,
DELETE_TOURNAMENT
} from '../actions/types';
export default (state = {}, action) => {
switch (action.type) {
case FETCH_TOURNAMENT:
return { ...state, [action.payload.id]: action.payload };
case FETCH_TOURNAMENTS:
return { ...state, [action.payload.id]: action.payload };
case CREATE_TOURNAMENT:
return { ...state, [action.payload.id]: action.payload };
case EDIT_TOURNAMENT:
return { ...state, [action.payload.id]: action.payload };
case DELETE_TOURNAMENT:
return _.omit(state, action.payload);
default:
return state;
}
};
To "optimistically" delete an item from state you'll need to immediately delete it from state to reflect the change right away in the UI. BUT you will need to add extra redux state to "hold" a pending delete with your backend. When the delete is successful you clear the held delete, if it fails you clear the held delete and add it back in to your regular data (and perhaps display some error message or toast, etc..).
I see you don't do the delete via redux, so use local component state and you'll have to filter your tournament data when rendering.
class SearchAndDisplay extends PureComponent {
componentDidMount() {
this.props.fetchTournaments();
}
state = {
searchCriteria: "",
isLoading: false,
optimisticTournaments: null // <-- state to hold temp "deleted" data
};
handleChange = event => {
this.setState({
searchCriteria: event.target.value
});
};
async handleDelete(id) {
console.log("delete id", id);
// optimistically remove element
this.setState({
optimisticTournaments: this.props.tournaments.filter(
item => item.id !== id
)
});
await axios
.delete(url + id)
.then(res => {
console.log(res.data);
// Need to create a call back to let parent know element was deleted
this.props.deleteSuccess(id);
})
.catch(err => {
console.log(err);
alert("Failed to delete");
})
.finally(() => {
this.setState({ optimisticTournaments: null });
});
}
formatDate(date) {
let options = {
year: "numeric",
month: "numeric",
day: "numeric",
hour: "numeric",
minute: "numeric",
second: "numeric",
hour12: false
};
let newDate = new Date(Date.parse(date));
let format = new Intl.DateTimeFormat("default", options).format(newDate);
return format;
}
handleChange = event => {
this.setState({ searchCriteria: event.target.value });
};
renderList() {
let tournmentsArray =
this.state.optimisticTournaments || this.props.tournaments;
const filterTournaments = tournmentsArray.filter(item =>
item.name.includes(this.state.searchCriteria)
);
if (filterTournaments === undefined || filterTournaments.length === 0) {
return (
<React.Fragment>
<div className="notFound">
Something went wrong.
<br />
<button
className="notFoundButton"
onClick={() => {
this.setState({ searchCriteria: "" });
}}
>
Retry
</button>
</div>
</React.Fragment>
);
} else {
return filterTournaments.map(item => (
<Item
key={item.name}
name={item.name}
organizer={item.organizer}
participants={Object.values(item.participants)}
game={item.game}
start={this.formatDate(item.startDate)}
>
<div className="buttonBar">
<EditTournament id={item.id} />
<button
className="button"
onClick={() => {
if (
window.confirm("Are you sure you want to delete this item?")
) {
this.handleDelete(item.id);
}
}}
>
Delete
</button>
</div>
</Item>
));
}
}
render() {
return (
<div className="container">
<input
onChange={this.handleChange}
className="input"
placeholder="Search..."
id="searchField"
value={this.state.searchCriteria}
/>
<div className="row">{this.renderList()}</div>
</div>
);
}
}
Here is how you can do using forceUpdate() (Since you don't want to use state):
import React, { Component } from 'react';
import { render } from 'react-dom';
class App extends Component {
constructor() {
super();
this.items = [
{id: 1, name: "item 1"},
{id: 2, name: "item 2"},
];
this.handleDelete = this.handleDelete.bind(this);
}
handleDelete(index) {
const newState = [...this.items];
delete newState[index];
// or
// newState.splice(index, 1);
this.items = newState;
this.forceUpdate();
}
render() {
return (
<div>
{
this.items.map((item, index) => {
return (
<div>
{item.name}
<button onClick={() => this.handleDelete(index)}>Delete item</button>
</div>
)
})
}
</div>
);
}
}
render(<App />, document.getElementById('root'));
Just pass the index in the map method :
...map((item, index) => ...);
Do it in the then() after your axios call.
Note that the documentation highly advice you to avoid using forceUpdate() when you can, so you really should use a state for this, I don't see any good reason for you not to use it here.
Here is a quick repro on Stackblitz.

ASP.NET Core API and React JS

I have created ASP.NET Core API and React CURD practice example. I am following this example
but I've used react semantic ui for view. I am new to react and ASP.NET any suggestion so that I can improve my code.
I am able to fetch,POST,PUT and DELETE customer record but there are some small issues or point that I don't know how to implement. Those are as following
1 - I have used Modal so I can open form as popup (AddCustomer is form to add and edit record) in that I have two functions to OPEN and CLOSE the Modal but I don't how to call them from Customer.js and also on successful POST,PUT, DELETE request.
2 - When I open FORM to ADD or EDIT record I am not able to store that in state. When I try to type in input field it does not store in name and address.
3 - Also you can see in Customer.js I am hiding the form and delete modal but I want to close them on POST, PUT and DELETE task completion.
This is Customer.js
import React from 'react';
import AddCustomer from './AddCustomer';
import CustomerView from './CustomerView';
import DeleteRecord from './DeleteRecord';
export default class Customer extends React.Component {
constructor(props) {
super(props);
this.state = {
isAddCustomer:false,
isEditCustomer:false,
isDeleteCustomer:false,
closeForm:false,
singleCustomer:{},
deleteId:{}
}
}
onCreate = () => {
console.log("is add customer true ")
this.setState({isAddCustomer:true})
}
onFormControl = () =>{
this.setState({
isAddCustomer:false,
isEditCustomer:false
})
}
onDeleteClick = customerId => {
const headerTitle = "Customer";
console.log("onDeleteClick")
this.setState({
isDeleteCustomer:true,
deleteId:{
ID:customerId,
title:headerTitle,
open:true
}
});
}
//New Customer record
onAddFormSubmit = data => {
console.log("In add form submit")
console.log(data)
let customerApi = 'https://localhost:44387/api/Customers';
let method = '';
if(this.state.isEditCustomer){
console.log("In Edit api")
console.log(this.state.editCustomerId)
customerApi = 'https://localhost:44387/api/Customers/' + this.state.editCustomerId;
method = 'PUT'
}else{
console.log("In Add api")
customerApi = 'https://localhost:44387/api/Customers';
method = 'POST'
}
const myHeader = new Headers({
'Accept':'application/json',
'Content-type':'application/json'
});
fetch(customerApi,{
method:method,
headers:myHeader,
body:JSON.stringify(data)
})
.then(res => res.json())
.then(
(result) => {
this.setState({
users:result,
isAddCustomer:false,
isEditCustomer:false
})
},(error) => {
this.setState({ error });
}
)
}
//Edit customer record
onEditCustomer = customerId => {
//Get ID, name and address
this.setState({
editCustomerId:customerId
});
const customerApi = 'https://localhost:44387/api/Customers/'+customerId;
const myHeader = new Headers({
'Accept':'application/json',
'Content-type':'application/json; charset=utf-8'
});
fetch(customerApi,{
method:'GET',
headers:myHeader
})
.then(res => res.json())
.then(
(result) => {
this.setState({
isEditCustomer:true,
isAddCustomer:false,
singleCustomer:{
customer:result,
isEditCustomer:true
}
})
},(error) => {
this.setState({ error });
}
)
}
//Delete Customer
onDeleteCustomer = customerId => {
const customerApi = 'https://localhost:44387/api/Customers/'+customerId;
const myHeader = new Headers({
'Accept':'application/json',
'Content-type':'application/json; charset=utf-8'
});
fetch(customerApi,{
method:'DELETE',
headers:myHeader
})
.then(res => res.json())
.then(
(result) => {
this.setState({
isDeleteCustomer:false
})
},(error) => {
this.setState({ error });
}
)
}
render() {
let form;
if(this.state.isAddCustomer && !this.state.isEditCustomer){
console.log("Add")
form = <AddCustomer onAddFormSubmit={this.onAddFormSubmit}
isAddCustomer = {this.state.isAddCustomer}
onFormControl = {this.onFormControl}/>
}else if(this.state.isEditCustomer && !this.state.isAddCustomer){
console.log("Edit")
form = <AddCustomer onAddFormSubmit={this.onAddFormSubmit}
singleCustomer = {this.state.singleCustomer}
onFormControl = {this.onFormControl}/>
}else if(this.state.isDeleteCustomer){
console.log("Delete")
console.log(this.state.deleteId)
form = <DeleteRecord onDeleteCustomer={this.onDeleteCustomer}
deleteId = {this.state.deleteId}
/>
}
return (
<div>
{form}
<br/>
<CustomerView
onEditCustomer = {this.onEditCustomer}
onCreate = {this.onCreate}
onDeleteClick = {this.onDeleteClick}/>
</div>
)
}
}
Here is CustomerView.js
import React from 'react';
import { Table, Button } from 'semantic-ui-react';
export default class CustomerView extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
deleteTitle: "customer",
isLoaded: false,
formClose: false,
singleCustomer: [],
users: []
}
}
//fetch data
componentDidMount() {
const customerApi = 'https://localhost:44387/api/Customers';
const myHeader = new Headers();
myHeader.append('Content-type', 'application/json');
myHeader.append('Accept', 'application/json');
myHeader.append('Origin', 'https://localhost:44387');
const options = {
method: 'GET',
myHeader
};
fetch(customerApi, options)
.then(res => res.json())
.then(
(result) => {
this.setState({
users: result,
isLoaded: true
});
},
(error) => {
this.setState({
isLoaded: false,
error
});
}
)
}
//Delete Customer
onDeleteCustomer = customerId => {
const { users } = this.state;
this.setState({
users: users.filter(customer => customer.customerId !== customerId)
});
const customerApi = 'https://localhost:44387/api/Customers/' + customerId;
const myHeader = new Headers({
'Accept': 'application/json',
'Content-type': 'application/json; charset=utf-8'
});
fetch(customerApi, {
method: 'DELETE',
headers: myHeader
})
.then(res => res.json())
.then(
(result) => {
this.setState({
})
}, (error) => {
this.setState({ error });
}
)
}
render() {
const { users } = this.state;
return (
<div>
<Button color='blue' onClick={() => this.props.onCreate()}>New Customer</Button>
<br/>
<br/>
<Table celled textAlign='center'>
<Table.Header>
<Table.Row>
<Table.HeaderCell>ID</Table.HeaderCell>
<Table.HeaderCell>Name</Table.HeaderCell>
<Table.HeaderCell>Address</Table.HeaderCell>
<Table.HeaderCell>Action</Table.HeaderCell>
<Table.HeaderCell>Action</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body >
{
users.map(user => (
<Table.Row key={user.customerId}>
<Table.Cell>{user.customerId}</Table.Cell>
<Table.Cell>{user.name}</Table.Cell>
<Table.Cell>{user.address}</Table.Cell>
<Table.Cell>
<Button color='blue' onClick={() =>
this.props.onEditCustomer(user.customerId)}>Edit</Button>
</Table.Cell>
<Table.Cell>
<Button color='red' onClick={() =>
this.props.onDeleteClick(user.customerId)}>Delete</Button>
</Table.Cell>
</Table.Row>
))
}
</Table.Body>
<Table.Footer>
<Table.Row>
<Table.HeaderCell colSpan='5'>
No of Pages
</Table.HeaderCell>
</Table.Row>
</Table.Footer>
</Table>
</div>
)
}
}
Here is AddCustomer.js
import React from 'react';
import { Button, Form, Modal } from 'semantic-ui-react';
export default class AddCustomer extends React.Component {
constructor(props) {
super(props);
this.state = {
showCreateForm: false,
addOrdit:false,
id: "",
name: "",
address: "",
formData: {},
record: {}
}
if (props.isAddCustomer){
this.state.showCreateForm = props.isAddCustomer;
}
else if (props.singleCustomer) {
console.log("Single customer")
console.log(props.singleCustomer)
this.state.id = props.singleCustomer.customer.customerId;
this.state.name = props.singleCustomer.customer.name;
this.state.address = props.singleCustomer.customer.address;
this.state.record = props.singleCustomer.customer;
this.state.showCreateForm = props.singleCustomer.isEditCustomer;
this.state.addOrdit = props.singleCustomer.isEditCustomer;
console.log(this.state.name)
}else if(props.closeForm){
this.state.showCreateForm = props.closeForm;
}
}
handleChangeName = event => {
const value = event.target.value;
this.setState({ name:value });
}
handleChangeAddress = event => {
const value = event.target.value;
this.setState({ address:value });
}
handleSubmit = event => {
event.preventDefault();
if(this.state.addOrdit){
this.setState({
record: {
customerId: this.state.id,
name: this.state.name,
address: this.state.address
}
});
this.props.onAddFormSubmit(this.state.record);
}else{
this.setState({
formData: {
name: this.state.name,
address: this.state.address
}
});
this.props.onAddFormSubmit(this.state.formData);
}
}
//On cancel button click close Create user form
closeCreateForm = () => {
this.setState({ showCreateForm: false })
this.props.onFormControl();
}
//Open Create new Customer form
openCreateCustomer = () => {
this.setState({ showCreateForm: true })
}
render() {
let formTitle;
if (this.state.id !== 0) {
formTitle = "Edit Customer";
} else {
formTitle = "New Customer";
}
return (
<div>
<Modal size='small'
closeOnTriggerMouseLeave={false}
open={this.state.showCreateForm}>
<Modal.Header>
{formTitle}
</Modal.Header>
<Modal.Content>
<Form onSubmit={this.handleSubmit}>
<Form.Field>
<label>Name</label>
<input type="text" placeholder='Name' name="name"
value={this.state.name}
onChange={this.handleChangeName} />
</Form.Field>
<Form.Field>
<label>Address</label>
<input type="text" placeholder='Address' name="address"
value={this.state.address}
onChange={this.handleChangeAddress} />
</Form.Field>
<br />
<Button type='submit' floated='right' color='green'>Create</Button>
<Button floated='right' onClick={this.closeCreateForm}
color='black'>Cancel</Button>
<br />
</Form>
</Modal.Content>
</Modal>
</div>
)
}
}
And last one DeleteRecord.js
import React from 'react';
import { Button, Modal, Icon } from 'semantic-ui-react';
export default class DeleteRecord extends React.Component {
constructor(props) {
super(props);
this.state = {
ID:'',
title: "",
open: false
}
if(props.deleteId){
console.log(props.deleteId)
this.state.ID = props.deleteId.ID;
this.state.title = props.deleteId.title;
this.state.open = props.deleteId.open;
}
}
//On cancel button click close Create user form
closeCreateForm = () => {
console.log("Clicked")
this.setState({ open: false })
}
//Open Create new Customer form
openCreateCustomer = () => {
this.setState({ open: true })
}
render() {
const title = "Delete " + this.state.title;
return (
<div>
<Modal size='small'
closeOnTriggerMouseLeave={false}
open={this.state.open}>
<Modal.Header>
{title}
</Modal.Header>
<Modal.Content>
<br />
Are you sure?
<br />
<br />
<Button floated='right' icon labelPosition='right' color='red'
value='true'
onClick={() => this.props.onDeleteCustomer(this.state.ID)}
>
<Icon name='close' />
Delete
</Button>
<Button floated='right' color='black'
value='false'
onClick={this.closeCreateForm}
>Cancel</Button>
<br />
<br />
</Modal.Content>
</Modal>
</div>
)
}
}
Try using mobx for managing state variables and axios for making calls to API this will resolve your problem.
sample example code for using mobx
import { observable, computed } from "mobx"
class OrderLine {
#observable price = 0
#observable amount = 1
#computed get total() {
return this.price * this.amount;
}
}
now you can import OrderLine class in your Js and you can use and manage the state of price, amount dynamically for rendering the UI
Go through below link
https://mobx.js.org/README.html

Showing two different components based on return value in react js

I have search function where on entering text it returns the object from an array(json data) and based on the condition (whether it as an object or not) I need to show two different components ie. the list with matched fields and "No matched results found" component.
class Search extends React.Component {
constructor(props) {
super(props);
this.state = {
searchTextData: '',
isSearchText: false,
isSearchOpen: false,
placeholderText:'Search Content',
filteredMockData: [],
dataArray: []
};
}
handleSearchChange = (event, newVal) => {
this.setState({ searchTextData: newVal })
if (newVal == '') {
this.setState({ clearsearch: true });
this.setState({
filteredMockData: []
});
this.props.onDisplayCloseIcon(true);
} else {
this.props.onDisplayCloseIcon(false);
searchData.searchResults.forEach((item, index, array) => {
this.state.dataArray.push(item);
});
this.setState({ filteredMockData: this.state.dataArray });
}
}
clearInput = () => {
this.setState({ searchTextData: '' })
}
isSearchText = () => {
this.setState({ isSearchText: !this.state.isSearchText });
}
onSearchClick = () => {
this.setState({ isSearchOpen: !this.state.isSearchOpen });
this.setState({ searchTextData: '' });
this.props.onDisplayCloseIcon(true);
}
renderSearchData = () => {
const SearchDatasRender = this.state.dataArray.map((key) => {
const SearchDataRender = key.matchedFields.pagetitle;
return (<ResultComponent results={ SearchDataRender } /> );
})
return SearchDatasRender;
}
renderUndefined = () => {
return ( <div className = "search_no_results" >
<p> No Recent Searches found. </p>
<p> You can search by word or phrase, glossary term, chapter or section.</p>
</div>
);
}
render() {
return ( <span>
<SearchIcon searchClick = { this.onSearchClick } />
{this.state.isSearchOpen &&
<div className = 'SearchinputBar' >
<input
placeholder={this.state.placeholderText}
className= 'SearchInputContent'
value = { this.state.searchTextData}
onChange = { this.handleSearchChange }
/>
</div>
}
{this.state.searchTextData !== '' && this.state.isSearchOpen &&
<span className='clearText'>
<ClearIcon className='clearIcon' clearClick = { this.clearInput }/>
</span>
}
{this.state.searchTextData !== '' && this.state.isSearchOpen &&
<div className="SearchContainerWrapper">
<div className = "arrow-up"> </div>
<div className = 'search_result_Container' >
<div className = "search_results_title" > <span> Chapters </span><hr></hr> </div>
<div className="search_show_text" >
<ul className ="SearchScrollbar">
{this.state.filteredMockData.length ? this.renderSearchData() : this.renderUndefined() }
</ul>
</div>
</div>
</div>}
</span>
);
}
}
Search.propTypes = {
intl: intlShape.isRequired,
onSearchClick: PropTypes.func,
isSearchBarOpen: PropTypes.func,
clearInput: PropTypes.func,
isSearchText: PropTypes.func
};
export default injectIntl(Search);
Search is my parent component and based on the matched values I need to show a resultComponent like
class ResultComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
render(){
console.log(this.props.renderSearchData(),'Helloooo')
return(<p>{this.props.renderSearchData()}</p>)
}
}
ResultComponent.propTypes = {
results: PropTypes.string.isRequired
};
I'm getting an error "renderSearchData is not an function".I'm new to react and Hope someone can help.
The only prop passed to ResultComponent component is results
So in ResultComponent Component Replace
this.props.renderSearchData()
With
this.props.results

ReactJs : how do I set up a simple conditional visible-hidden rule?

I have a collection of components with and add and remove button.
I'd like to hide/grey out these buttons when the collection is too small and too big.
This bit of code picked up from here seems like it would do the job
:
render() {
return (
{ this.props.isGreaterThanOne && (
<div>
this only shows up if the checkboxField Field is checked
</div>
) }
);
}
And I could simply update a boolean within my increment and decrement functions like so :
let isGreaterThanOne = true;
this.setState({
collection: this.state.collection.splice(this.state.collection, this.state.collection.length - 1),
});
if (this.state.collection.length > 1) {
isGreaterThanOne = true;
} else {
isGreaterThanOne = false;
}
return { isGreaterThanOne };
Now I'm consious the above is probably as garbage as it gets. All suggestions welcome. I think the variable should be decalred globally but I don't know how to do it.
Basically I just want there to be a variable that is set to false whenever the remove line should be hid (only one line left) and true when there are two lines or more.
Same deal with another variable traking when the collection reaches 15 or something.
Right now even if I just declare isGreaterThanOne as a global const equal to true the div doesn't show up.
As per #Denialos 's demand here is my full component (with his suggestions) :
/* ************************************* */
/* ******** IMPORTS ******** */
/* ************************************* */
import React, { Component } from 'react';
import UUID from 'node-uuid';
import { Card, CardBlock, Button, InputGroup, Input } from 'reactstrap';
import ProviderInfos from '../ProviderInfos/ProviderInfos';
/* ************************************* */
/* ******** VARIABLES ******** */
/* ************************************* */
/* ************************************* */
/* ******** COMPONENT ******** */
/* ************************************* */
export default class PretzelStandComponent extends Component {
constructor(props) {
super(props);
this.state = {
inputPretzel: [],
isGreaterThanOne: true
};
this.incrementPretzel = this.incrementPretzel.bind(this);
this.decrementPretzel = this.decrementPretzel.bind(this);
}
componentDidMount() {
this.incrementPretzel();
}
incrementPretzel() {
const uuid = require('uuid/v1');
uuid();
const inputPretzel = this.state.inputPretzel;
if (this.state.inputPretzel.length > 1) {
this.state.isGreaterThanOne = true;
} else {
this.state.isGreaterThanOne = false;
}
this.setState({
inputPretzel: inputPretzel.concat(<InputGroup>
<Input placeholder="Pretzel" key={uuid} /><ProviderInfos /></InputGroup>),
});
}
decrementPretzel() {
if (this.state.inputPretzel.length > 1) {
this.state.isGreaterThanOne = true;
} else {
this.state.isGreaterThanOne = false;
}
this.setState({
inputPretzel: this.state.inputPretzel.splice(this.state.inputPretzel, this.state.inputPretzel.length - 1),
});
}
render() {
return (
<Card>
<CardBlock className="main-table">
<fieldset>
<legend>Pretzels</legend>
{this.state.inputPretzel}
<button onClick={this.incrementPretzel}>Ajouter un Pretzel</button>
{ this.props.isGreaterThanOne && (
<div>
<button onClick={this.decrementPretzel}>Enlever un Pretzel</button>
</div>
) }
</fieldset>
<Button color="secondary">Options</Button>{' '}
<Button id="btn">Exécuter</Button>
</CardBlock>
</Card>
);
}
}
Problem here is you try to mutate a prop in your component. As already said, Never mutate your state or props this way.
here is a possible solution to your problem :
render() {
const { inputPretzel } = this.state;
const isGreaterThanOne = inputPretzel && inputPretzel.length > 0;
return (
<Card>
<CardBlock className="main-table">
<fieldset>
<legend>Pretzels</legend>
{inputPretzel}
<button onClick={this.incrementPretzel}>Ajouter un Pretzel</button>
{isGreaterThanOne && (
<div>
<button onClick={this.decrementPretzel}>Enlever un Pretzel</button>
</div>
) }
</fieldset>
<Button color="secondary">Options</Button>{' '}
<Button id="btn">Exécuter</Button>
</CardBlock>
</Card>
);
}
Don't mutate state directly, you should call setState.
NEVER mutate this.state directly, as calling setState() afterwards may
replace the mutation you made. Treat this.state as if it were
immutable.
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState
and calls may be batched for performance gains. setState() will
always trigger a re-render unless conditional rendering logic is
implemented in shouldComponentUpdate().
If mutable objects are being used and the logic cannot be implemented
in shouldComponentUpdate(), calling setState() only when the new state
differs from the previous state will avoid unnecessary re-renders.
Font: What the difference of this.state and this.setstate in ReactJS?
Possible solution for your problem, Using immutability-helper:
import update from 'immutability-helper';
.....
.....
incrementPretzel() {
const uuid = require('uuid/v1');
uuid();
const inputPretzel = this.state.inputPretzel;
if (this.state.inputPretzel.length > 1) {
this.setState({ isGreaterThanOne: true });
else {
this.setState({ isGreaterThanOne: false });
}
this.setState({
inputPretzel: inputPretzel.concat(<InputGroup>
<Input placeholder="Pretzel" key={uuid} /><ProviderInfos /></InputGroup>),
});
}
decrementPretzel() {
const inputPretzel = this.state.inputPretzel;
if (this.state.inputPretzel.length > 1) {
this.setState({ isGreaterThanOne: true });
} else {
this.setState({ isGreaterThanOne: false });
}
this.setState((prevState) => ({
data: update(prevState.data, {$splice: [[-1, 1]]})
})
}
render() {
return (
<Card>
<CardBlock className="main-table">
<fieldset>
<legend>Pretzels</legend>
<button onClick={() => this.incrementPretzel}>Ajouter un Pretzel</button>
{ this.state.isGreaterThanOne &&
<div>
<button onClick={() => this.decrementPretzel}>Enlever un Pretzel</button>
</div>
}
</fieldset>
<Button color="secondary">Options</Button>
<Button id="btn">Exécuter</Button>
</CardBlock>
</Card>
);
}

Resources