I have a row component that contains all information about a project and a switch that shows the active status of the project (T or F).
render() {
const dateDisplay = moment(this.props.createdAt).format('MMM YYYY');
return (
<tr
className="experiment-list__row"
onMouseOver={() => this.props.onRowHovered(this.props.rowItems.id)}
>
<td>{this.props.rowItems.name}</td>
<td>{this.props.rowItems.owner}</td>
<td>{dateDisplay}</td>
<td className="experiment-list--col__switch">
<Switch
color="primary"
checked={this.props.rowItems.status}
onChange={()=>{this.handleSubmit(this.props.rowItems.id, this.props.rowItems.status)}}
/>
</td>
</tr>
);
}
It looks like this. When I click the switch it is supposed to toggle and change the status based on the current status. The handleSubmit does it for me.
handleSubmit(rowID: any, rowStatus: any) {
console.log(rowID, rowStatus)
makeMutation(UpdateExperimentQuery, {
update: {
id: rowID,
data: {
status: !rowStatus
},
},
})
.then(responseData => {
console.log(responseData)
})
.catch(err => {
console.log(err);
});
}
It updates the data correctly.
However, it does not get updated immediately even though the checked attribute of the switch represents the status of the row.
When I refresh it, it changes, but I want it to show it immediately since that's the point of using react.
Please help
EDIT
Displays the Info
<div className="experiments-list-container">
<List
onRowHovered={this.getExperimentID}
rowItems={this.state.experimentData}
/>
</div>
This is a different Component ListRow
const List = props => {
return (
<table className="experiment-list">
<tbody>
<ListHeader />
{props.rowItems.map((data, i) => (
<ListRow key={i} rowItems={data} onRowHovered={props.onRowHovered} />
))}
</tbody>
</table>
);
};
The render of ListRow
export class ListRow extends Component<ListRowProps, ListRowState> {
constructor(props) {
super(props);
this.state = {
experimentData: [],
status: props.status //unused right now
};
this.handleSubmit = this.handleSubmit.bind(this);
}
// also not used now
componentWillUpdate() {
if (this.state.status !== this.props.status) {
this.setState({
status: this.props.status,
});
}
}
handleSubmit(rowID: any, rowStatus: any) {
console.log(rowID, rowStatus)
makeMutation(UpdateExperimentQuery, {
update: {
id: rowID,
data: {
status: !rowStatus
},
},
})
.then(responseData => {
console.log(responseData)
})
.catch(err => {
console.log(err);
});
}
render() {
const dateDisplay = moment(this.props.createdAt).format('MMM YYYY');
return (
<tr
className="experiment-list__row"
onMouseOver={() => this.props.onRowHovered(this.props.rowItems.id)}
>
<td>{this.props.rowItems.name}</td>
<td>{this.props.rowItems.owner}</td>
<td>{dateDisplay}</td>
<td className="experiment-list--col__switch">
<Switch
color="primary"
checked={this.props.rowItems.status}
onChange={()=>{this.handleSubmit(this.props.rowItems.id, this.props.rowItems.status)}}
/>
</td>
</tr>
);
}
}
EDIT
changeStatus(rowID, rowStatus) {
// close if the selected employee is clicked
console.log(rowID, rowStatus)
makeMutation(UpdateExperimentQuery, {
update: {
id: rowID,
data: {
status: !rowStatus
},
},
})
.then(responseData => {
setTimeout(() => {
this.componentWillMount();
},1000);
console.log('responseData', responseData)
})
.catch(err => {
console.log(err);
});
}
this updates the data and gets called in the List component.
componentWillMount() {
let filteredData = []
//this.props.fetchExperiments();
makeQuery(ExperimentsListQuery)
.then(responseData => {
this.setState(prevState => ({
experimentData: responseData.Experiments,
}));
for(var i = 0; i < this.state.experimentData.length; i++) {
console.log(this.state.experimentData[i])
if(this.state.experimentData[i].status === this.state.viewActive) {
filteredData.push(this.state.experimentData[i])
}
}
this.setState({
filteredExpData: filteredData
})
})
.catch(err => {
//actions.setSubmitting(false);
console.log(err);
});
}
this fetches/filters the data
Related
how can i sort my data by title? This is my code:
And how can i call this 3 calls at the same time? I tried with "Promise All" but didn't work for me..
Thanks for help! ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
import './Style.css';
class PostList extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
data: [],
items: []
};
}
componentDidMount() {
fetch(this.props.url)
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
data: result.data,
items: result.items
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { error, isLoaded, data } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Please wait...</div>;
} else {
return (
<div> <table style={{ width: '100%'}}>
{data.items.map(item => (
<div>
<tr>
<th>Title:</th> <td>{item.title}</td>
</tr>
<tr>
<th>Artist:</th> <td>{item.artist}</td>
</tr>
<tr>
<th>Label:</th> <td>{item.label}</td>
</tr>
<tr>
<th>Year:</th> <td>{item.year}</td>
</tr>
</div>
))}
</table>
</div>
);
}
}
}
export default PostList
AND
import React, {Component} from 'react';
import PostList from './PostList';
class index extends Component {
render () {
return (
<div style={{
display: 'inline-flex'
}}>
<PostList url={"https://api.netbet.com/development/randomFeed?website=casino&lang=eu&device=desktop&source=list1"} />
<PostList url={"https://api.netbet.com/development/randomFeed?website=casino&lang=eu&device=desktop&source=list2"} />
<PostList url={"https://api.netbet.com/development/randomFeed?website=casino&lang=eu&device=desktop&source=list3"} />
</div>
)
}
}
export default index;
To sort the items just use Array.prototype.sort and String.prototype.localeCompare to compare the titles:
const sortedItems = data.items.sort((item1, item2) => item1.title.localeCompare(item2.title));
// and then render sorted items
{sortedItems.map(item => (...)}
If you want to do three calls at the same time you really need to use Promise.all. It can be done in parent component, or inside of PostList. So you can do the following:
const LIST_URLS = [
'https://api.netbet.com/development/randomFeed?website=casino&lang=eu&device=desktop&source=list1',
'https://api.netbet.com/development/randomFeed?website=casino&lang=eu&device=desktop&source=list2',
'https://api.netbet.com/development/randomFeed?website=casino&lang=eu&device=desktop&source=list3'
];
...
async componentDidMount() {
this.setState({ isLoading: true });
try {
const lists = await Promise.all(LIST_URLS.map((url) => {
return fetch(this.props.url).then(res => res.json());
});
const list = lists.flat();
const sortedList = data.items.sort((item1, item2) => item1.title.localeCompare(item2.title));
this.setState({ list: sortedList });
} catch (error) {
this.setState({ error });
} finally {
this.setState({ isLoading: false });
}
}
render() {
// here you can just render single list with all the items, passing them as a prop
const { isLoading, error, items } = this.state;
return (
<div>
{isLoading && 'Loading...'}
{items && <PostList list={this.state.list} />}
{error && 'Failed to fetch'}
</div>
);
}
If all 3 API data should be sorted, then you have to use like below. If you want individual API responses to be sorted use sort before updating the state.
class PostList extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
data: [],
items: [],
};
this.callAPi = this.callAPi.bind(this);
}
callAPi(url) {
return fetch(url)
.then((res) => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
data: [...result.data].sort((item1, item2) =>
item1.title.localeCompare(item2.title)
),
items: [...result.items].sort((item1, item2) =>
item1.title.localeCompare(item2.title)
),
});
},
(error) => {
this.setState({
isLoaded: true,
error,
});
}
);
}
componentDidMount() {
Promise.all(props.urls.map(this.callAPi));
}
render() {
const { error, isLoaded, data } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Please wait...</div>;
} else {
return (
<div>
<table style={{ width: "100%" }}>
{data.items.map((item) => (
<div>
<tr>
<th>Title:</th> <td>{item.title}</td>
</tr>
<tr>
<th>Artist:</th> <td>{item.artist}</td>
</tr>
<tr>
<th>Label:</th> <td>{item.label}</td>
</tr>
<tr>
<th>Year:</th> <td>{item.year}</td>
</tr>
</div>
))}
</table>
</div>
);
}
}
}
USE
<PostList
urls={[
"https://api.netbet.com/development/randomFeed?website=casino&lang=eu&device=desktop&source=list1",
"https://api.netbet.com/development/randomFeed?website=casino&lang=eu&device=desktop&source=list2",
"https://api.netbet.com/development/randomFeed?website=casino&lang=eu&device=desktop&source=list3",
]}
/>;
I develop cards in react js that card automatically generated based on API data but that all card shows one below other and I want to design that card in row fashion. I try many ways to design that card in row way but it is not done. I try inline CSS, some react-bootstrap class. So please give me some suggestions about how I can design this card.
class App extends React.Component {
elements = [];
state = {
data:[ null ]
};
getBikes = () => {
try {
return axios.get('URL')
} catch (error) {
console.error(error)
}
}
constructor(props) {
super(props);
this.props = props;
//this.elements=["one", "two", "three", "four"];
}
componentDidMount() {
const breeds = this.getBikes()
.then(response => {
if (response) {
console.log(response.data.message)
var arr = (response.data.message);
//var elements = [];
for (var i = 0; i < arr.length; i++) {
console.log(arr[i].bikeId)
this.elements.push(<div>
<Cards value={arr[i]} />
</div>);
}
this.setState({ data: arr[0].bikeId })
}
})
.catch(error => {
console.log(error)
})
}
render() {
console.log("printitng")
//const array = [1,2];
return (
<div style={{ display: "flex" }}>
<h1>{'This will always render'}</h1>
{this.state && this.state.data &&
this.state.map((item, index) => {
return <div key={index} style={{ width: "300px" }}>
<Cards elements={this.elements} /> //pass your props value
</div>
})
// < div >
// <Cards elements={this.elements} /> //pass your props value
// </div>
}
</div>
)
}
}
There's just one correction. You've pushed the elements of array with div which is a block element. You need push the elements with span or you can give class to the div and change from block to flex or inline block.
componentDidMount() {
const breeds = this.getBikes()
.then(response => {
if (response) {
console.log(response.data.message)
var arr = (response.data.message);
//var elements = [];
for (var i = 0; i < arr.length; i++) {
console.log(arr[i].bikeId)
// Changed from div to span
this.elements.push(<span>
<Cards value={arr[i]} />
</span>);
}
this.setState({ data: arr[0].bikeId })
}
})
.catch(error => {
console.log(error)
})
}
render() {
console.log("printitng")
return (
<div>
<h1>{'This will always render'}</h1>
{this.state && this.state.data &&
<div>
<Cards elements={this.elements} /> //pass your props value
</div>
}
</div>
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
bikeId : null,
data : []
}
}
getBikes = () => {
try {
return axios.get('URL')
} catch (error) {
console.error(error)
}
}
componentDidMount() {
const breeds = this.getBikes()
.then(response => {
if (response) {
this.setState({ data: response.data.message, bikeId: response.data.message[0].bikeId })
}
})
.catch(error => {
console.log(error)
})
}
render() {
return (
<div style={{ display: "flex" }}>
{
this.state.data.map((item, index) => {
return <div key={index} style={{ width: "300px" }}>
<Cards value={item} />
</div>
})
}
</div>
)
}
}
give parent class flex property then you can add as much child in a single row you can add.
I have a list like this:
<div className="doubleCol">
{this.state.symptoms.map(item => (
<ListItem key={item.ObjectID}>
<input type="checkbox" className="sympSelect" />
{item.name}
</ListItem>
))}
</div>
All the items rendered have checkbox and I want it to filter a different list elsewhere on the page based on which boxes are checked. To do that I need the checkboxes to change the state and pass the new state to a method which is supposed to filter and display only those items on the second list with id's associated to items on the first list.
From what I have read it shouldn't matter for this purpose if the checkboxes are controlled or uncontrolled.
class Home extends React.Component {
state = {
conditions: [],
symptoms: [],
selectedSymptom: []
}
componentDidMount() {
this.getConditionsMethod();
this.getSymptomsMethod();
}
getConditionsMethod = () => {
API.getConditions()
.then(data => {
console.log(data);
data.data.sort((a, b) => a.name.localeCompare(b.name))
this.setState({
conditions: data.data
})
})
.catch(err => console.log(err))
};
filterConditionsMethod = () => {
API.getConditions()
.then(data => {
console.log(data);
data.data.sort((a, b) => a.name.localeCompare(b.name));
this.setState({
selectedSymptom: data.data
})
})
.catch(err => console.log(err))
};
But I am kind of stuck on how to structure the onChange for when the box is checked and how to make that implement the filter.
Here is you solution you can add onChange event for checkbox and filter your records as selectedSymptoms and symptoms. Please check code is
import React, { Component } from "react";
class Home extends Component {
constructor(props) {
super(props);
this.state = {
conditions: [],
symptoms: [
{ ObjectID: 1, name: "xyz" },
{ ObjectID: 2, name: "pqr" }
],
selectedSymptom: [],
checked: ""
};
}
updateCheckBox = (event, item) => {
if (event.target.checked) {
let selectedList = this.state.selectedSymptom;
selectedList.push(item);
this.setState({
...this.state,
checked: this.state.checked == "checked" ? "" : "checked",
selectedSymptom: selectedList
});
} else {
const symptomss = this.state.selectedSymptom.filter(element => {
if (element.ObjectID != data.ObjectID) {
return item;
}
});
this.setState({
...this.state,
checked: "",
selectedSymptom: symptomss
});
}
};
render() {
return (
<div className="doubleCol">
{this.state.symptoms.map(item => (
<ListItem key={item.ObjectID}>
<input
type="checkbox"
className="sympSelect"
onChange={this.updateCheckBox(e, item)}
id="symptoms_id"
defaultChecked={this.state.checked}
/>
{item.name}
</ListItem>
))}
</div>
);
}
}
export default Home;
The parent component Dashboard holds the state for every ListItem I add to my Watchlist. Unfortunately, every time I am adding an Item, it gets added to the DB, but only shows up when I refresh the browser.
class UserDashboard extends React.Component {
state = {
data: []
}
componentWillMount() {
authService.checkAuthentication(this.props);
}
isLoggedIn = () => {
return authService.authenticated()
}
getAllCoins = () => {
//fetches from backend API
}
addWishlist = () => {
this.getAllCoins()
.then(things => {
this.setState({
data: things
})
})
console.log("CHILD WAS CLICKED")
}
componentDidMount() {
this.getAllCoins()
.then(things => {
this.setState({
data: things
})
})
}
render() {
return (
<div className="dashboard">
<h1>HI, WELCOME TO USER DASHBOARD</h1>
<SearchBar
addWishlist={this.addWishlist}
/>
<UserWatchlist
data={this.state.data}
/>
</div>
);
}
}
The User Watchlist:
class UserWatchlist extends React.Component {
constructor(props) {
super(props)
}
// componentDidUpdate(prevProps) {
// if (this.props.data !== prevProps.data) {
// console.log("CURRENT", this.props.data)
// console.log("PREVs", prevProps.data)
// }
// }
render() {
return (
<div>
<h2>These are tssssyou are watching:</h2>
<ul className="coin-watchlist">
{
this.props.data.map((coin, idx) => {
return <ListItem key={idx}
coin={coin.ticker}
price={coin.price}
/>
})
}
</ul>
</div>
)
}
}
The search Bar that shows potential Items to watch over:
class SearchBar extends React.Component {
constructor(props) {
super(props)
this.state = {
coins: [],
searchValue: ""
}
}
searchHandler = e => {
e.preventDefault()
const value = e.target.value
this.setState({
searchValue: value
});
if (value === "") {
this.setState({
coins: []
})
} else {
this.getInfo()
}
}
getInfo = () => {
// Searches the API
}
addWishlist = () => {
this.props.addWishlist();
}
render() {
const {coins, searchValue} = this.state
return (
<div className="coin-search">
<form>
<input
type="text"
className="prompt"
placeholder="Search by ticker symbol"
value={searchValue}
onChange={this.searchHandler}
/>
</form>
<ul className="search-suggestions">
{
coins.filter(searchingFor(searchValue)).map( coin =>
<Currency
coin={coin}
addWishlist={this.addWishlist}
/>
)
}
</ul>
</div>
);
}
}
And the actual Currency that gets clicked to be added:
class Currency extends React.Component {
addToWatchlist = () => {
// POST to backend DB to save
};
fetch("/api/add-coin", settings)
.catch(err => {
return err
})
}
clickHandler = () => {
this.addToWatchlist()
this.props.addWishlist()
}
render() {
return(
<div className="search-results">
<li>
<h3> { this.props.coin.currency } </h3>
<button
className="add-to-list"
onClick={this.clickHandler}
>
+ to Watchlist
</button>
</li>
</div>
)
}
}
As you can see, I am sending props down all the way down to child. When I click the button to Add to Watchlist, I see the console.log message appear, saying "CHILD WAS CLICKED". I've even tried just calling the method to fetch from backend API again.
Also, in UserWatchlist, I've tried a componentDidUpdate, but both prevProps and this.props show the very same array of data. Somewhere in the chain, my data is getting lost.
This is also my first time posting a question here, so if it can be improved, I am happy to add extra details and contribute something to this community
You probably forgot to wait for addToWatchlist to complete:
addToWatchlist = () => {
// POST to backend DB to save
return fetch("/api/add-coin", settings)
.catch(err => {
return err
})
}
clickHandler = () => {
this.addToWatchlist().then(() => {
this.props.addWishlist()
})
}
I'm trying to create small app based on Json server package which will help me to remember movies I want to watch when I have free time, want to learn React and Axios so I'm doing it with these technologies , Idea is when I click on add movie button - movie will be added to Json database,
when click on delete - particular movie will be deleted
and when click on the list - I will be able to edit text,
Delete works if I do something like http://localhost:3000/movies/1, to show what id should it delete, but is there any way to set it? To delete the list connected to button I'm clicking at? something like http://localhost:3000/movies/"id"? I will be grateful for any help as I totally don't have any idea how to move on with it
import React from 'react';
import ReactDom from 'react-dom';
import axios from 'axios';
import List from "./list.jsx";
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
name:'',
type:'',
description:'',
id:'',
movies: [],
}
}
handleChangeOne = e => {
this.setState({
name:e.target.value
})
}
handleChangeTwo = e => {
this.setState({
type:e.target.value
})
}
handleChangeThree = e => {
this.setState({
description:e.target.value
})
}
handleSubmit = e => {
e.preventDefault()
const url = `http://localhost:3000/movies/`;
axios.post(url, {
name: this.state.name,
type: this.state.type,
description:this.state.description,
id:this.state.id
})
.then(res => {
// console.log(res);
// console.log(res.data);
this.setState({
movies:[this.state.name,this.state.type,this.state.description, this.state.id]
})
})
}
handleRemove = (e) => {
const id = this.state.id;
const url = `http://localhost:3000/movies/`;
// const id = document.querySelectorAll("li").props['data-id'];
e.preventDefault();
axios.delete(url + id)
.then(res => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
})
}
// editMovie = e => {
// const url = `http://localhost:3000/movies/`;
// e.preventDefault();
// const id = e.target.data("id");
// axios.put(url + id, {
// name: this.state.name,
// type: this.state.type,
// description:this.state.description,
// })
// .then(res => {
// console.log(res.data);
// })
// .catch((err) => {
// console.log(err);
// })
// }
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="movie" onChange={this.handleChangeOne}/>
<input type="text" placeholder="type of movie" onChange={this.handleChangeTwo}/>
<textarea cols={40} rows={5} placeholder="description of the movie" onChange={this.handleChangeThree}></textarea>
<input type="submit" value="Add movie"></input>
<List removeClick={this.handleRemove} editClick={this.editMovie}/>
</form>
)
}
}
export default Form
List:
import React from 'react';
import ReactDom from 'react-dom';
import axios from 'axios';
class List extends React.Component{
constructor(props){
super(props)
this.state = {
movies: [],
}
}
componentDidMount() {
const url = `http://localhost:3000/movies`;
console.log(url);
axios.get(url)
.then(res => {
console.log(res.data);
const movies = res.data;
this.setState({
movies: movies
})
})
.catch((err) => {
console.log(err);
})
}
// editMovie =(e) => {
// console.log("it works with edit!");
// if (typeof this.props.editClick === "function") {
// this.props.editClick(e)
// } else {
// console.log("Doesn't work with edit");
// }
// }
removeMovie =(e) => {
console.log("it works with remove!");
if (typeof this.props.removeClick === "function") {
this.props.removeClick(e)
} else {
console.log("Doesn't work with remove");
}
}
render(){
let movies = this.state.movies.map(e =>
<ul onClick={this.editMovie}>
<li data-id={e.id}>
{e.name}
</li>
<li data-id={e.id}>
{e.type}
</li>
<li data-id={e.id}>
{e.description}
</li>
<button type="submit" onClick={this.removeMovie}>Delete</button>
</ul>)
return(
<div>
{movies}
</div>
)
}
}
export default List;
Json part
{
"movies": [
{
"id": 1,
"name": "Kongi",
"type": "drama",
"description": "movie about monkey"
},
{
"id": 2,
"name": "Silent Hill",
"type": "thriller",
"description": "movie about monsters"
},
{
"name": "Harry potter",
"type": "fantasy",
"description": "movie about magic and glory",
"id": 3
}
]
}
You could pass the movie object to the removeMovie function in your List component and pass that to the this.props.removeClick function. You could then take the id of the movie to use for your request, and remove the movie from state if the DELETE request is successful.
Example
class Form extends React.Component {
handleRemove = movie => {
const url = `http://localhost:3000/movies/${movie.id}`;
axios
.delete(url)
.then(res => {
this.setState(previousState => {
return {
movies: previousState.movies.filter(m => m.id !== movie.id)
};
});
})
.catch(err => {
console.log(err);
});
};
// ...
}
class List extends React.Component {
removeMovie = (e, movie) => {
e.preventDefault();
if (this.props.removeClick) {
this.props.removeClick(movie);
}
};
// ...
render() {
return (
<div>
{this.state.movies.map(movie => (
<ul onClick={this.editMovie}>
<li data-id={movie.id}>{movie.name}</li>
<li data-id={movie.id}>{movie.type}</li>
<li data-id={movie.id}>{movie.description}</li>
<button type="submit" onClick={e => this.removeMovie(e, movie)}>
Delete
</button>
</ul>
))}
</div>
);
}
}
An simple example using hooks:
const URL = 'https://jsonplaceholder.typicode.com/users'
const Table = () => {
const [employees, setEmployees] = React.useState([])
React.useEffect(() => {
getData()
}, [])
const getData = async () => {
const response = await axios.get(URL)
setEmployees(response.data)
}
const removeData = (id) => {
axios.delete(`${URL}/${id}`).then(res => {
const del = employees.filter(employee => id !== employee.id)
setEmployees(del)
})
}
const renderHeader = () => {
let headerElement = ['id', 'name', 'email', 'phone', 'operation']
return headerElement.map((key, index) => {
return <th key={index}>{key.toUpperCase()}</th>
})
}
const renderBody = () => {
return employees && employees.map(({ id, name, email, phone }) => {
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{email}</td>
<td>{phone}</td>
<td className='opration'>
<button className='button' onClick={() => removeData(id)}>Delete</button>
</td>
</tr>
)
})
}
return (
<>
<h1 id='title'>React Table</h1>
<table id='employee'>
<thead>
<tr>{renderHeader()}</tr>
</thead>
<tbody>
{renderBody()}
</tbody>
</table>
</>
)
}
ReactDOM.render(<Table />, document.getElementById('root'));