I'm making an application using Laravel and React.
I made an event in laravel to display data in real time and I'm using "React Transition Group" to add an animation to the added data.
The problem is, when I'm adding data normally without this package, the data is showing correctly, but once added, the animation and data are working fine except when repeating the same data.
For example if I add in {patient.nom} and {patient.prenom} values such as John & Doe. They will appear, but if I put them again, they will not, but in database they are registered perfectly.
After some tests I'm pretty sure it has to do with the key in
<CSSTransition
key={patient.nom,patient.prenom}
timeout={500}
classNames="item"><CSSTransition>
Whole component :
import React,{Component} from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
import Echo from "laravel-echo";
import {
ListGroup,
Button,
} from 'react-bootstrap';
import {
CSSTransition,
TransitionGroup,
} from 'react-transition-group';
import './styles.css';
class Patient extends React.Component {
constructor(props) {
super(props)
this.state = {
patients : [],
};
}
componentDidMount() {
axios.get('api/patients')
.then(response => {this.setState({patients: response.data})})
.catch(err => console.log(err));
window.Echo.channel('home')
.listen('NewPatient', newPatientData => {
this.setState({
patients: this.state.patients.concat(newPatientData)
})
}, e => {
console.log("Error", e)
})
}
render() {
return (
<div>
<TransitionGroup>
{this.state.patients.slice(0).reverse().map(patient =>
<CSSTransition
key={patient.nom,patient.prenom}
timeout={500}
classNames="item">
<ListGroup.Item>
{patient.nom} {patient.prenom}
</ListGroup.Item>
</CSSTransition>
)}
</TransitionGroup>
</div>
)
}
}
export default Patient;
Solved. As I read in an article:
Generate a unique id for every item and use it as key when
rendering the list.
So I didn't have an unique id in my list, because the event that was broadcasting did not send id.
So in the final result in the key I should have key={patient.id} , like that every element in the list would have a unique id from database.
Related
I made a request to google books API, trying to retrieve the volumes (a.k.a books) from my bookshelf. But the response I get contains only 10 items whereas on the shelf I have 17 items.
Could this be that my code is faulty, or maybe a restriction from google ?
see my code for making the call and for mapping it.
My api.js
import React, { Component } from "react"
import request from 'superagent';
import BookList from './BookList';
class Book extends Component{
constructor(props){
super(props);
this.state = {
books: [],
}
}
componentDidMount(){
request.get("https://www.googleapis.com/books/v1/users/101###############/bookshelves/4/volumes?key=AIzaSyDNMnPGw3################-PecbhU")
.query(null)
.then((data) =>{
console.log(data);
this.setState({books: [...data.body.items]})
})
}
render(){
return(
<div>
<BookList books={this.state.books}/>
</div>
);
}
}
export default Book
And here i map
import React, { Component } from "react"
import BookCard from './bookcard';
const BookList = (props) => {
if(!props){
return null;
}
return(
<div>
{
props && props.books?.map((book, i) => {
return <div class="grid-container">
<BookCard
key={i}
image={book.volumeInfo.imageLinks.thumbnail}
title={book.volumeInfo.title}
author={book.volumeInfo.authors}
url={book.volumeInfo.canonicalVolumeLink}
rating={book.volumeInfo.averageRating}
/>
</div>
})
}
</div>
)
}
export default BookList
As the docs say:
Pagination
You can paginate the volumes list by specifying two values in the parameters for the request:
startIndex - The position in the collection at which to start. The index of the first item is 0.
maxResults - The maximum number of results to return. The default is 10, and the maximum allowable value is 40.
So you can add maxResults=40 to your query to get more results at once.
I have the following code that display a table's data from Laravel using axios in React.
The data is displayed in real time. How can I add a fade-in animation each time a new element is added ? https://socket.io/ shows exactly what I want to do in the example on the right.
Note that the element in the li tag is added from an event that is fired up from a creation controller.
The component :
import React,{Component} from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
import Echo from "laravel-echo";
class Patient extends React.Component {
constructor(props) {
super(props)
this.state = {
patients : [],
};
}
componentDidMount() {
axios.get('api/patients')
.then(response => {this.setState({patients: response.data})})
.catch(err => console.log(err));
window.Echo.channel('home')
.listen('NewPatient', newPatientData => {
this.setState({
patients: this.state.patients.concat(newPatientData)
})
}, e => {
console.log("Error", e)
})
}
render() {
return (
<div>
<ul> { this.state.patients.slice(0).reverse().map(patient => <li>{patient.nom}</li>)} </ul>
</div>
)
}
}
export default Patient;
You can do this pretty easily with CSS animations. I've created an example below for you and if you check out the CSS, you'll see the keyframe animation which is then used by the .fadeIn selector and that class is then applied to the <li> element.
https://codesandbox.io/s/dreamy-frog-r6sr8?file=/src/styles.css
I just started teaching myself ReactJS a few weeks ago and I'm stuck trying to figure out why my API gets consistently hit in an infinite loop after selecting a value from a dropdown. I have a search component called StateSearch.js that is being rendered in the StatePolicyPage.js component.
In StatePolicyPage.js I call <StateSearch parentCallback={this.callbackFunction} /> so that I can get the value the user picked from the dropdown and set the state. In StateSearch.js I'm passing the selected value using props.parentCallback(response.data)
The problem is that an infinite loop occurs for some reason and my Rails API keeps getting called over and over instead of just returning the data one time.
(StateSearch.js) search component
import React, { useState } from 'react'
import Select from 'react-select'
import makeAnimated from 'react-select/animated';
import axios from 'axios';
import statesJSON from '../../helpers/states';
// uses 'react-select'
export default function StateSearch(props) {
const [americanState, setAmericanState] = useState();
// if a state was selected from the dropdown
if (americanState) {
axios.get("http://localhost:3001/get_stuff", {
params: {
state: americanState.value
}
}).then(response => {
// the response back from the Rails server
if (response.status === 200) {
props.parentCallback(response.data); // send data back up to parent
}
}).catch(error => {
console.log("Error fetching the state ", americanState.value, error);
})
event.preventDefault();
}
// the dropdown select box for states.
return (
<div>
<Select
options={statesJSON}
placeholder="Select a State"
onChange={setAmericanState}
noOptionsMessage={() => 'Uh-oh nothing matches your search'}
className=""
components={makeAnimated()}
isSearchable
isClearable={true}
/>
</div>
)
}
(StatePolicyPage.js) the component that the search results should be passed to
import React, { Component } from 'react'
import Navigation from './Navigation';
import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import StateSearch from './search/StateSearch';
export default class StatePolicyPage extends Component {
constructor(props) {
super(props);
this.state = {
id: '',
stateName: '',
updatedAt: '',
createdAt: ''
}
}
callbackFunction = (childData) => {
console.log(childData);
this.setState({
id: childData.id,
stateName: childData.state_name,
updatedAt: childData.updated_at,
createdAt: childData.created_at
})
}
render() {
return (
<div>
<Navigation
isLoggedIn={this.props.loggedInStatus}
user={this.props.user}
handleLogoutClick={this.props.handleLogoutClick}
handleLogout={this.props.handleLogout}
/>
<Container>
{/* get the dropdown value from the StateSearch back */}
<StateSearch parentCallback={this.callbackFunction} />
<div>
<Row>
{ this.state.id }
</Row>
</div>
</Container>
</div>
)
}
}
Always use useEffect() hook for asynchronous tasks.
useEffect(() => {
// if a state was selected from the dropdown
if (americanState) {
axios.get("http://localhost:3001/get_stuff", {
params: {
state: americanState.value
}
}).then(response => {
// the response back from the Rails server
if (response.status === 200) {
props.parentCallback(response.data); // send data back up to parent
}
}).catch(error => {
console.log("Error fetching the state ", americanState.value, error);
})
}
}, [americanState]);
This line looks to me like it could cause an infinite loop:
components={makeAnimated()}
I'm not entirely sure what this function is doing, but when passing functions to another component you can't directly invoke them.
Try replacing the above line with this:
components={makeAnimated}
or with this:
components={() => makeAnimated()}
I'm implementing pagination functionality by semantic-ui-react.
I can implement pagination component itself, but can't implement onPageChange to set activePage and control number of pages displayed.
I use react for client side functionality.
Also I use semantic-ui-react for css framework.
All contents of array is listed on single page now.
But I want to implement pagination and limit to display 5 contents on single page.
I somehow understand I need to use onPageChange, but don't know how to implement to achieve that goal.
import React from 'react';
import {Link} from 'react-router-dom';
import {Grid, Segment, Container, Header, Pagination} from 'semantic-ui-react';
import axios from 'axios';
import {Article} from '../articleData';
interface ArticleState {
articles: Article[];
}
class List extends React.Component<{}, ArticleState> {
constructor(props: {}) {
super(props);
this.state = {
articles: [],
};
this.serverRequest = this.serverRequest.bind(this);
this.btnClick = this.btnClick.bind(this);
}
serverRequest() {
axios
.get('/api/articles')
.then(response => {
this.setState({articles: response.data});
})
.catch(response => console.log('ERROR!! occurred in Backend.'));
}
btnClick(event: React.MouseEvent<HTMLAnchorElement>, data: object) {
}
componentDidMount() {
this.setState({articles: []});
this.serverRequest();
}
render() {
return (
<Container style={{marginTop: '7em'}} text>
<Grid columns={1} divided="vertically">
<Grid.Row>
{(this.state.articles || []).map(function(articleData, i) {
return (
<Grid.Column>
<Segment>
<Header as="h1">{articleData.title}</Header>
<p>{articleData.content}</p>
<Link to={`/detail/${articleData.id}`}>
continue reading
</Link>
</Segment>
</Grid.Column>
);
})}
</Grid.Row>
</Grid>
<Pagination
defaultActivePage={5}
totalPages={Math.floor(this.state.articles.length / 2) + 1}
//onPageChange={this.btnClick}
/>
</Container>
);
}
}
export default List;
I expect the pagination functionality to limit number of displayed content to 5 on single page.
But actually I don't know how to implement this functionality.
I resolved this issue.
Here is the code:
import React from 'react';
import {Link} from 'react-router-dom';
import {
Grid,
Segment,
Container,
Header,
Pagination,
PaginationProps,
Icon,
} from 'semantic-ui-react';
import axios from 'axios';
import {Article} from '../articleData';
interface ArticleState {
articles: Article[];
articleDatas: Article[];
begin: number;
end: number;
activePage: number;
}
class List extends React.Component<{}, ArticleState> {
constructor(props: {}) {
super(props);
this.state = {
articles: [],
articleDatas: [],
begin: 0,
end: 5,
activePage: 1,
};
this.serverRequest = this.serverRequest.bind(this);
this.btnClick = this.btnClick.bind(this);
}
async serverRequest() {
const res = await axios.get('/api/articles');
this.setState({articles: res.data});
}
async btnClick(
event: React.MouseEvent<HTMLAnchorElement>,
data: PaginationProps
) {
await this.setState({activePage: data.activePage as number});
await this.setState({begin: this.state.activePage * 5 - 5});
await this.setState({end: this.state.activePage * 5});
this.setState({
articleDatas: this.state.articles.slice(this.state.begin, this.state.end),
});
}
async componentDidMount() {
this.setState({articles: []});
await this.serverRequest();
this.setState({
articleDatas: this.state.articles.slice(this.state.begin, this.state.end),
});
}
render() {
return (
<Container style={{marginTop: '3em'}} text>
<Grid columns={1} divided="vertically">
<Grid.Row>
{(this.state.articleDatas || []).map(function(articleData, i) {
return (
<Grid.Column>
<Segment>
<Header as="h1">{articleData.title}</Header>
<p>{articleData.content}</p>
<Link to={`/detail/${articleData.id}`}>
continue reading
</Link>
</Segment>
</Grid.Column>
);
})}
</Grid.Row>
</Grid>
<Pagination
defaultActivePage={1}
totalPages={Math.ceil(this.state.articles.length / 5)}
onPageChange={this.btnClick}
/>
</Container>
);
}
}
export default List;
From the semantic-ui docs, onPageChange is a function with two arguments:
event - React's original SyntheticEvent
data - An object containing all props
Looking inside the data object, there is a key named activePage which represents the new page number when the pagination is changed.
Here's a very basic example that demonstrates this, by logging the newly selected page number to the console.
<Pagination
defaultActivePage={5}
totalPages={10}
onPageChange={(event, data) => console.log(data.activePage)}
/>
However, I think you're expectation of how Pagination in Semantic UI works may not be correct. The Pagination component purely renders a clickable list of page numbers, it does not handle the display of results or limits the number of results displayed.
This is something that is generally handled in the API. A common approach is to have a ?page=x query string parameter (or sometimes ?offset=) in the URL. You could then use the activePage value from the Semantic Pagination component to indicate to the server which page of results to return.
I'm trying to pass the data from this axios call into a child component, Hero. Despite having passed down the props and made a successful axios call it won't actually make it into the Hero div.
When I console.log on the child component it claims to have the data but then fails to push it to the champions array so I can't use it. Any ideas?
Edit:
I'll add in here that I do have react-router installed in this project however this data is being passed around across one "view" and not multiple pages.
This is the parent component
import React, { Component } from 'react';
import axios from 'axios';
import './assets/stylesheets/screen.css';
import Hero from './Hero';
import Info from './Info';
class Home extends Component {
constructor(props){
super(props);
this.state = { champions: [] };
}
componentDidMount() {
axios.get(
'https://api.pinterest.com/v1/boards/gasulliv/pose-
references/pins/?access_token=AQjW6hDdAF0egwEesZA6oJbqP0XQFQ-
m6_jg2RpErKPqdSA7cQAAAAA&limit=100&fields=id%2Clink%2Cnote%2
Curl%2Coriginal_link%2Cimage').then(champions => {
this.setState({ champions });
console.log(champions);
});
}
render() {
return (
<div>
<Hero champions = {this.state.champions} />
<Info />
</div>
);
}
}
export default Home;
And this is child component (at this console log I get two answers, one claiming it has the data and another claiming it does not):
import React from 'react';
import Header from './Header';
import 'bootstrap/dist/css/bootstrap.min.css';
import './assets/stylesheets/screen.css';
const Hero = (props) => {
console.log(props);
return (
<div className = "jumbotron kindred">
<Header />
<div className = "textHolder">{ props.champions.length }</div>
</div>
)
}
export default Hero;
You have to access the data in the data key response.data
Try the following.
axios.get('https://api.pinterest.com/v1/boards/gasulliv/pose-references/pins/?access_token=AQjW6hDdAF0egwEesZA6oJbqP0XQFQ-m6_jg2RpErKPqdSA7cQAAAAA&limit=100&fields=id%2Clink%2Cnote%2Curl%2Coriginal_link%2Cimage')
.then((response) => {
this.setState({
champions: response.data
})
})
.catch((error) => {
// Do something with the error
})
Thanks for help but it turns out the issue had to do with the fact that I had the router installed. Likely I just need to pass that data around through the router instead of the pages.
Kudos for the help!