I have an app with React front and Spring backend. I use Axios to fetch from the back. I have 2 class components with tables and I can access them via a menu component (in componentDidMount and componentDidUpdate only). I use all the possible precautions against infinite loops (loaded state and isMounted with a custom name). It works in the first component which I access after logging in. However, the second component (which is logically the same as the first, just has another entity to fetch) keeps requesting with axios until i go there (i see it in the network tab of my browser). Why can it be? it is definitely not mounted and console.logs don't work from there but while I'm on first it keeps requesting on and on (and it doesn't receive anything I guess, it is 0 bytes at this time)
import React, { Component } from 'react'
import {Link} from 'react-router-dom';
import axios from 'axios'
import "react-table/react-table.css";
import ReactTable from 'react-table';
import {Button, ButtonToolbar} from 'react-bootstrap';
import { LinkContainer } from "react-router-bootstrap";
import AddCalculationsModal from './AddCalculationsModal';
import UpdateCalculationsModal from './UpdateCalculationsModal';
import Cluster from './Cluster';
import Select from 'react-select/src/Select';
export default class Calculations extends Component {
isCMounted = false;
constructor(props) {
super(props)
this.state = {
items: [],
selected: null,
addModalShow: false,
updateModalShow: false,
updateId: null,
buttonOn: false,
page: 0,
elements: 0,
loaded: false
}
}
componentDidMount() {
this.isCMounted = true;
if(!this.state.loaded){
this.load();
}
};
componentDidUpdate() {
if(!this.state.loaded){
this.load();
}
};
componentWillUnmount(){
this.isCMounted = false;
}
increasePage = () => {
this.setState({
page: this.state.page + 1
})
}
decreasePage = () => {
this.setState({
page: this.state.page - 1
})
}
load = async () => {
await axios.get(`calculations?page=${this.state.page}&elements=${this.state.elements}`)
.then(res => {
if (this.isCMounted && this.state.items.id === res.data.id){
this.setState({items: res.data})
}
});
if(this.state.selected != null && this.isCMounted) {
this.setState({buttonOn: true})
}
this.setState({loaded: true})
}
setId = (id) => {
const idValue = this.state.items[id].id;
if (this.isCMounted)
this.setState({updateId: idValue});
}
deleteRow = (id) => {
const index = this.state.items.findIndex(item => {
return item.id === this.state.items[id].id})
const idValue = this.state.items[id].id
axios.delete(`calculations/${idValue}`).then(
res => {
this.load();
}
)
this.state.items.splice(index, 1)
this.load();
}
render() {
let addModalClose = () => this.setState({addModalShow: false});
let updateModalClose = () => this.setState({updateModalShow: false});
return (
<div>
<h3>Calculations</h3>
<ReactTable
columns={
[
{
Header: "ID",
accessor: "id"
},
{
Header: "Name",
accessor: "name"
},
{
Header: "Creation Date",
accessor: "dateCreate"
},
{
Header: "Update Date",
accessor: "dateUpdate"
},
{
Header: "User",
accessor: "userId"
}
]
}
data={this.state.items}
filterable
showPagination={false}
getTrProps={(state, rowInfo) => {
if (rowInfo && rowInfo.row) {
return {
onClick: (e) => {
this.setState({
selected: rowInfo.index
})
},
style: {
background: rowInfo.index === this.state.selected ? '#00afec' : 'white',
color: rowInfo.index === this.state.selected ? 'white' : 'black'
}
}
}else{
return {}
}
}}
>
</ReactTable>
<ButtonToolbar>
<Button variant="primary" onClick={() => {
this.decreasePage();
this.load();
}}>PREVIOUS PAGE</Button>
<Button variant="primary" onClick={() => {
this.increasePage();
this.load();
}}>NEXT PAGE</Button>
</ButtonToolbar>
<ButtonToolbar>
<Button variant="primary" onClick={() => this.setState({addModalShow: true})}>
Add Calculation
</Button>
<Button variant="primary" onClick={() => {
this.setId(this.state.selected);
this.setState({updateModalShow: true})}} disabled={this.state.buttonOn ? false : true}>
Update Calculation
</Button>
<Button variant="danger" onClick={() => {
this.deleteRow(this.state.selected);
}}>DELETE</Button>
<Link to={`/calculations/${this.state.items[this.state.selected] && this.state.items[this.state.selected].id}`}>
<Button variant="warning" disabled={this.state.buttonOn ? false : true}>Cluster</Button>
</Link>
<AddCalculationsModal
show={this.state.addModalShow}
onHide={addModalClose}
calculation={this.state.items[this.state.selected]}
/>
<UpdateCalculationsModal
show={this.state.updateModalShow}
onHide={updateModalClose}
calculation={this.state.items[this.state.selected] && this.state.items[this.state.selected].id}
calcname={this.state.items[this.state.selected] && this.state.items[this.state.selected].name}
/>
</ButtonToolbar>
</div>
)
}
}
And
import React, { Component } from 'react'
import axios from 'axios'
import "react-table/react-table.css";
import ReactTable from 'react-table';
import {Button, ButtonToolbar} from 'react-bootstrap';
import AuthenticationService from '../service/AuthenticationService';
export default class Calculations extends Component {
isCMounted = false;
constructor(props) {
super(props)
this.state = {
items: [],
selected: null,
updateId: null,
loaded: false
}
}
componentDidMount() {
this.isCMounted = true;
if(!this.state.loaded) {
this.load();
}
};
componentDidUpdate() {
if(!this.state.loaded) {
this.load();
}
};
componentWillUnmount() {
this.isCMounted = false;
}
load = async () => {
if(this.isCMounted && !this.state.loaded) {
await axios.get('calculation-types')
.then(res => {
console.log(this.isCMounted)
if (this.isCMounted && this.state.items.id === res.data.id){
this.setState({items: res.data})
}
});
this.setState({loaded: true})
}
}
setId = (id) => {
const idValue = this.state.items[id].id;
if (this.isCMounted)
this.setState({updateId: idValue});
}
render() {
return (
<div>
<h3>Calculation Types</h3>
<ReactTable
columns={
[
{
Header: "ID",
accessor: "idType",
width: 100,
minWidth: 100,
maxWidth: 100
},
{
Header: "Name",
accessor: "name"
}
]
}
data={this.state.items}
filterable
showPagination={false}
getTrProps={(state, rowInfo) => {
if (rowInfo && rowInfo.row) {
return {
onClick: (e) => {
this.setState({
selected: rowInfo.index
})
},
style: {
background: rowInfo.index === this.state.selected ? '#00afec' : 'white',
color: rowInfo.index === this.state.selected ? 'white' : 'black'
}
}
}else{
return {}
}
}}
>
</ReactTable>
</div>
)
}
}
are my components. Menu is a normal link. after login i appear on the first with menu on top.
Have you tried moving this.setState({loaded: true}) into the axios response callback block? Since you're awaiting the fetch request, I wonder if the this.setState({items: res.data} that you have in the callback block is causing an infinite componentDidUpdate loop that causes load to be repeatedly called without ever having the chance to arrive at the this.setState({loaded: true}) in the final line of load.
load = async () => {
if(this.isCMounted && !this.state.loaded) {
await axios.get('calculation-types')
.then(res => {
console.log(this.isCMounted)
if (this.isCMounted && this.state.items.id === res.data.id){
this.setState({ items: res.data, loaded: true })
}
});
}
}
Related
I want to slide out a React component when it umounts. I am using CSSTransition for the animation which works great for mounting, but not unmounting. Somehow I need to delay the unmount process. All the off-the-shelf solutions sadly do not work for me. I am removing an element by doing a post request and then actually removing it in the UI with a SignalR callback.
To make my sequence more clear, I created a sequence diagram:
This is my code right now:
Board.tsx
import React from 'react';
import { Config } from 'util/config';
import { container } from 'tsyringe';
import { AppState } from 'store';
import { connect } from 'react-redux';
import { BoardState } from 'store/board/types';
import { BoardHubService } from 'services/hubs/boardHub.service';
import { BoardElementViewModel } from 'models/BoardElementViewModel';
import { BoardViewModel } from 'models/BoardViewModel';
import { BoardElement } from './boardElement/boardElement';
import { HttpService } from 'services/http.service';
import { setActiveBoard } from 'store/board/actions';
import './board.scss'
import { mapToType } from 'helpers/helpers';
import { TransitionGroup } from 'react-transition-group';
interface BoardProps {
activeBoardState: BoardState;
setActiveBoard: typeof setActiveBoard;
}
interface LocalBoardState {
boardElements: Array<BoardElementViewModel>
}
class Board extends React.Component<BoardProps, LocalBoardState> {
private config: Config;
private httpService: HttpService;
private boardHubService: BoardHubService;
constructor(props: any) {
super(props);
this.config = container.resolve(Config);
this.boardHubService = container.resolve(BoardHubService);
this.httpService = container.resolve(HttpService);
this.state = {
boardElements: []
}
}
async componentDidMount() {
// If there was any active board on page load...
if (this.props.activeBoardState.boardId) {
await this.loadBoardElements();
}
this.boardHubService.getConnection().on('SwitchedBoard', (response: BoardViewModel | null) => {
console.log(response);
this.setState({
boardElements: (response) ? response.elements : []
});
this.updateSiteTitle(response);
});
this.boardHubService.getConnection().on('ReceiveElement', (response: BoardElementViewModel) => {
let elements = this.state.boardElements;
elements.unshift(response);
this.setState(() => ({
boardElements: elements
}))
});
this.boardHubService.getConnection().on('RemoveElement', (response: string) => {
let elements = this.state.boardElements;
let element = mapToType<BoardElementViewModel>(elements.find(x => x.id === response));
elements.splice(elements.indexOf(element), 1);
this.setState(() => ({
boardElements: elements
}))
});
}
/**
* Load the elements from the board that was already active on page load.
*/
private async loadBoardElements() {
await this.httpService.getWithAuthorization<Array<BoardElementViewModel>>(`/boards/${this.props.activeBoardState.boardId}/elements`)
.then((response: Array<BoardElementViewModel>) => {
this.setState({
boardElements: response
});
})
.catch((e) => console.warn(e));
}
private updateSiteTitle(board: BoardViewModel | null) {
if (board != null) {
document.title = `${board.name} | ${this.config.siteName}`;
}
else {
document.title = this.config.siteName;
}
}
render() {
return (
<>
{this.props.activeBoardState.boardId != null
?
<div className="board-elements">
{this.state.boardElements.map((element: BoardElementViewModel, index) => {
return (
<BoardElement
key={index}
id={element.id}
// TODO: Use number from server
number={element.elementNumber}
user={element.user}
direction={element.direction}
note={element.note}
imageId={element.imageId}
createdAt={element.createdAt}
/>
)
})}
</div>
:
<div className="select-board-instruction">
<h1>Please select or create a board.</h1>
</div>
}
</>
)
}
}
const mapStateToProps = (state: AppState) => ({
activeBoardState: state.activeBoard
});
export default connect(mapStateToProps, { setActiveBoard })(Board);
BoardElement.tsx
import React from 'react';
import { UserViewModel } from 'models/UserViewModel';
import { Direction } from 'models/Direction';
import './boardElement.scss';
import { dateToReadableString } from 'helpers/helpers';
import { Config } from 'util/config';
import { container } from 'tsyringe';
import { HttpService } from 'services/http.service';
import $ from 'jquery'
import { CSSTransition } from 'react-transition-group';
interface BoardElementProps {
id: string;
number: number;
user: UserViewModel;
// TODO: Use direction Enum
direction?: Direction;
note?: string;
imageId?: string;
createdAt: Date;
}
interface BoardElementState {
show: boolean;
}
export class BoardElement extends React.Component<BoardElementProps, BoardElementState> {
private config: Config;
private httpService: HttpService;
private ref: any;
constructor(props: BoardElementProps) {
super(props);
this.state = {
show: false,
}
this.config = container.resolve(Config);
this.httpService = container.resolve(HttpService);
}
getReadableDirection(direction: Direction) {
// TODO: Richtingen vertalen
switch (direction) {
case Direction.North: return 'Noord';
case Direction.NorthEast: return 'Noordoost';
case Direction.East: return 'Oost';
case Direction.SouthEast: return 'Zuidoost';
case Direction.South: return 'Zuid';
case Direction.SouthWest: return 'Zuidwest';
case Direction.West: return 'West';
case Direction.NorthWest: return 'Noordwest';
}
}
removeElement() {
this.httpService.deleteWithAuthorization(`/boards/elements/${this.props.id}`).then(() => {
}, (error) => {
console.warn(error);
});
}
componentDidMount() {
setTimeout(() => {
this.setState(() => ({
show: true
}));
}, 500);
}
componentWillUnmount() {
this.setState(() => ({
show: false
}));
}
render() {
return (
<CSSTransition in={this.state.show} timeout={200} classNames={{
enter: 'animation-height',
enterDone: 'animation-height',
exit: ''
}}>
<div className="animation-wrapper animation-height-0" >
<div className="board-element" >
<div className="board-element-header">
<span className="board-element-number">{this.props.number}</span>
<span className="board-element-creator">{this.props.user.username}</span>
<i className="fas fa-trash ml-auto delete-icon" onClick={() => this.removeElement()}></i>
</div>
<div className="board-element-body">
{this.props.imageId
? <img className="board-element-image" src={`${this.config.apiUrl}/content/${this.props.imageId}`} />
: <p className="board-element-message">{this.props.note}</p>
}
</div>
<div className="board-element-footer">
{this.props.direction &&
<div className="board-element-direction">
<i className="fas fa-location-arrow direction mr-2"></i>{this.getReadableDirection(this.props.direction)}
</div>
}
<time className="board-element-timestamp" dateTime={this.props.createdAt.toString()}>{dateToReadableString(this.props.createdAt)}</time>
</div>
</div>
</div>
</CSSTransition >
)
}
}
For better illustration take a look at this GIF:
https://gyazo.com/3c933851ecec39029f25d4df3a136c2a
That is using jQuery in another project of mine. That is what I want to achive in React.
You can delay unmounting the component. Write a hoc and use a setTimeout. Maintain a state say shouldRender.
hoc
function delayUnmounting(Component) {
return class extends React.Component {
state = {
shouldRender: this.props.isMounted
};
componentDidUpdate(prevProps) {
if (prevProps.isMounted && !this.props.isMounted) {
setTimeout(
() => this.setState({ shouldRender: false }),
this.props.delayTime
);
} else if (!prevProps.isMounted && this.props.isMounted) {
this.setState({ shouldRender: true });
}
}
render() {
return this.state.shouldRender ? <Component {...this.props} /> : null;
}
};
}
usage
function Box(props) {
return (
<BoxWrapper isMounted={props.isMounted} delay={props.delay}>
✨🎶✨🎶✨🎶✨🎶✨
</BoxWrapper>
);
}
const DelayedComponent = delayUnmounting(Box);
See complete code in the demo
Read this article on medium
To-Do-List
When I try to edit my created task, I see some modifications, but only in local State. When I look at the data of the global state, nothing change, the data remains the same as after creating the tasks object.
It is also interesting to note that when case EDIT_TASK has worked , action.id = values from Input, and action.task = undefined
P.S: Put all the component code below, maybe there was a mistake somewhere.
P.S: Sorry for ENG
Component's code
import React from 'react'
import s from "./../../App.module.css";
class Item extends React.Component {
state = {
statusChange: false,
task: this.props.task
}
activeStatusChange = () => {
this.setState( {
statusChange: true
}
);
}
deActivateStatusChange = () => {
this.setState( {
statusChange: false
}
);
this.props.editTask(this.props.task)
}
onStatusChange = (e) => {
this.setState({
task: e.target.value
})
}
render(){
return (
<div className={s.item}>
<span onClick={this.props.editStatus} className={s.statusTask}>
{this.props.status ? <img src="https://img.icons8.com/doodle/48/000000/checkmark.png"/>
: <img src="https://img.icons8.com/emoji/48/000000/red-circle-emoji.png"/>}
</span>
{ this.state.statusChange
? <input onChange={this.onStatusChange} autoFocus={true} onBlur={this.deActivateStatusChange} value={this.state.task} />
: <span className={this.props.status === true ? s.task : s.taskFalse} onClick={this.activeStatusChange}> {this.state.task} </span>}
<span onClick={this.props.deleteTask} className={s.close}><img src="https://img.icons8.com/color/48/000000/close-window.png"/></span>
</div>
)
}
}
export default Item;
Reducer's code
import React from 'react'
import shortid from 'shortid';
const ADD_TASK = 'ADD_TASK'
const EDIT_TASK = 'EDIT_TASK'
const initialState = {
tasks: []
};
const mainReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TASK: {
return {
...state,
tasks: [{
id: shortid.generate(),
task: action.task,
status: false
}, ...state.tasks]
}
}
case EDIT_TASK: {
return {
...state,
tasks: state.tasks.filter((t) => t.id === action.id ? {...t, task: action.newTask} : t)
}
}
default:
return state
}
}
//window.store.getState().mainReducer.tasks
export const addTask = task => ({type: 'ADD_TASK', task});
export const editTask = (id,newTask) => ({type: 'EDIT_TASK', id, newTask})
export default mainReducer;
Parent's component:
import React from "react";
import s from "./../../App.module.css";
import CurrentTasks from "../current-tasks";
import FilterButtonTasks from "../filter-button-tasks";
import ListTasks from "../tasks-list";
class SetForm extends React.Component {
constructor(props) {
super(props);
this.state = {
text: ''
}
}
onInputChange = event => {
this.setState({
[event.target.name]: event.target.value
})
}
handleSubmit = event => {
event.preventDefault();
if(this.state.text === '') {
return undefined
}
this.props.addTask(this.state.text)
this.setState({
text: ''
})
}
filterTasks = (tasks, activeFilter) => {
switch (activeFilter) {
case 'done': {
return tasks.filter(task => task.status);
}
case 'active': {
return tasks.filter(task => !task.status)
}
default:
return tasks;
}
}
render() {
const currentTasks = this.filterTasks(this.props.tasks, this.props.filter);
return (
<div>
<form onSubmit={this.handleSubmit}>
<div>
<input name={"text"} onChange={this.onInputChange} value={this.state.text}placeholder={"Set your task"} className={s.setTask}/>
<button onClick={this.handleSubmit} className={s.add}>ADD</button>
<button onClick={this.props.removeAllTasks} className={s.clearAll}>Clear</button>
</div>
</form>
<CurrentTasks tasks={this.props.tasks}/>
<ListTasks currentTasks={currentTasks} editStatus={this.props.editStatus} deleteTask={this.props.deleteTask} editTask={this.props.editTask}/>
<FilterButtonTasks currentTasks={currentTasks} changeFilter={this.props.changeFilter} removeAllDone={this.props.removeAllDone}/>
</div>
)
}
}
export default SetForm;
one more:
import React from 'react'
import Item from './SetItem/item'
const ListTasks = ({currentTasks,editStatus,deleteTask,editTask}) => {
return (
currentTasks.map(t => (<Item editStatus={() => editStatus(t.id)}
deleteTask={() => deleteTask(t.id)}
key={t.id} task={t.task} status={t.status} editTask={editTask}/>))
)
}
export default ListTasks;
Since, you are only updating the local state onStatusChange the state does not get updated in global state. So on deActivateStatusChange you need to call this.props.editTask with updated state, that is this.state.task
deActivateStatusChange = () => {
this.setState({
statusChange: false
});
this.props.editTask(this.state.task); // change is here
};
The problem is in your EDIT_TASK reducer:
Change
state.tasks.filter((t) => t.id === action.id ? {...t, task: action.newTask} : t)
To
state.tasks.map((t) => t.id === action.id ? {...t, task: action.newTask} : t)
map will update the object, not filter
Code should be:
case EDIT_TASK: {
return {
...state,
tasks: state.tasks.map((t) => t.id === action.id ? {...t, task: action.newTask} : t)
}
}
Also it seems like you are not passing id and newTask to editTask action:
const ListTasks = ({ currentTasks, editStatus, deleteTask, editTask }) => {
return currentTasks.map(t => (
<Item
editStatus={() => editStatus(t.id)}
deleteTask={() => deleteTask(t.id)}
key={t.id}
task={t.task}
status={t.status}
editTask={(newTask) => editTask(t.id, newTask)} // change current code to this
/>
));
};
Description:
Currently trying to integrate react-images into a project using React and Gatsby. Done a lot of tinkering with different possible fixes, and I am unable to locate the issue. It seems like the issue either lies within mapping the images or tracking the currentIndex value properly.
ContainerLightBox:
import React from 'react'
import { StaticQuery, graphql } from "gatsby"
import LightBox from './LightBox'
const ContainerLightBox = () => (
<StaticQuery
query={graphql`
query {
images: allFile(
filter: { sourceInstanceName: { eq: "gallery" } }
sort: { fields: name, order: ASC }
) {
edges {
node {
childImageSharp {
fluid(maxWidth: 1000) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
`}
render={data => <LightBox images={data.images.edges} />}
/>
)
export default ContainerLightBox
LightBox:
import React, { Component, Fragment } from 'react'
import Img from 'gatsby-image'
import Lightbox from 'react-images'
export default class LightBox extends Component {
constructor(props) {
super(props)
this.state = {
lightboxIsOpen: false,
currentImage: 0
}
}
toggleLightbox = ( i, e ) => {
e.preventDefault()
this.setState(
{
lightboxIsOpen: !this.state.lightboxIsOpen,
},
() => {
this.state.active
? this.setState({
currentImage: i,
})
: this.setState({
currentImage: 0,
})
}
)
}
gotoPrevious = () => {
this.setState({
currentImage: this.state.currentImage - 1
})
}
gotoNext = () => {
this.setState({
currentImage: this.state.currentImage + 1
})
}
handleClickImage = () => {
if (this.state.currentImage === this.props.images.length - 1) {
return this.gotoNext()
}
}
render() {
const { images } = this.props
return (
<Fragment>
{images.map(( photo, i ) => {
return (
<a
key={i}
href={photo.node.childImageSharp.fluid.src}
onClick={e => this.toggleLightbox( i, e )}
className="photo-container"
>
<Img
fluid={photo.node.childImageSharp.fluid}
style={{ width: '100%', height: '100%' }}
/>
</a>
)
})}
<Lightbox
images={images.map( photo => ({
src: photo.node.childImageSharp.fluid.src,
srcSet: photo.node.childImageSharp.fluid.srcSet
}))}
currentImage={this.state.currentImage}
isOpen={this.state.lightboxIsOpen}
onClickPrev={this.gotoPrevious}
onClickNext={this.gotoNext}
onClickImage={this.handleClickImage}
onClose={this.toggleLightbox}
showImageCount={false}
backdropClosesModal={true}
/>
</Fragment>
)
}
}
Error:
TypeError: Cannot read property '0' of undefined
Carousel.getViewData
1574 | var currentIndex = _this2.state.currentIndex;
1575 |
1576 |
1577 | return views[currentIndex];
1578 | };
1579 |
1580 | this.focusViewFrame = function () {
setState is in use but DataTables doesn't refresh after Add or Edit function.
Please focus on:
listOfCurrency
Function ws
Function onClickSubmitAddCurrency
Component JSTable
Tag in JSTable.js
Currency.js
componentWillMount() {
this.setState(
{
listOfCurrency: [],
currency: { currency: '', symbol: '', status: '' },
myCurrencyRate: { exchangeRate: 0, adminFee: 0 },
currencyRateRequest:{exchangeRate:'',processFee:'',earnrate:'',earnAdminFee:''},
myCurrency: { currency:''},
isAdmin:false,
message: '',
snackbar: {
open: false,
vertical: null,
horizontal: null,
},
}
);
var checkAccessRightUrl=properties.domain+properties.checkAccessRightUrl;
this.wsCheckUrlAccess(checkAccessRightUrl,'Currency');
var wsListUrl = properties.domain + properties.currencyList;
this.ws(wsListUrl, false, false, 'get', null, false,'list');
var populateMyMembershipCurrencyRate = properties.domain + properties.populateMembeshipCurrencyRate;
this.wsbind(populateMyMembershipCurrencyRate,'currencyRate');
var myMembershipCurrency = properties.domain + properties.showMyMembershipCurrency;
this.wsbind(myMembershipCurrency,'myMembershipCurrency');
var checkIsAdminUrl = properties.domain + properties.populateCheckIsAdmin;
this.wsbind(checkIsAdminUrl,'checkIsAdmin');
}
ws(wsurl, jsonLibrary, toggle, process, senditem, snackbar,processName) {
var accessToken = sessionStorage.getItem('accessToken');
var reqs;
if (process === 'post') {
reqs = Request.post(wsurl)//wsurl
} else if (process === 'get') {
reqs = Request.get(wsurl)//wsurl
}
reqs.set('Authorization', 'Bearer ' + accessToken)
.set('Content-Type', 'application/json')
if (senditem != null) {
reqs.send(senditem)
}
reqs.end((err, res) => {
if (res.status === 200) {
if(processName === 'currencyRate'){
this.setState(
{
isLoading:false,
currencyRateRequest: res.body,
message: (res.body===null?'':res.body.message),
snackbar: {
vertical: 'top',
horizontal: 'right',
open: snackbar
},
}
);
}else{
this.setState(
{
isLoading:false,
listOfCurrency: (jsonLibrary ? JSON.parse(JSON.stringify(res.body.responses)) : res.body),
message: (res.body===null?'':res.body.message),
snackbar: {
vertical: 'top',
horizontal: 'right',
open: snackbar
},
}
);
}
if (toggle) {
this.togglePrimary();
}
} else {
this.checkSession(res);
console.log('do logout function here');
console.log(err);
}
});
}
onClickSubmitAddCurrency() {
var wsListUrl = properties.domain + properties.addCurrency;
this.ws(wsListUrl, true, true, 'post', this.state.currency, true);
};
render() {
return (
<div className="animated fadeIn">
<CardBody>
<JSTable id='#currencyTable'
dataSet={this.state.listOfCurrency} columns={columns}
onEdit={this.onClickGotoEdit.bind(this)} onDelete={this.onClickSubmitDeleteCurrency.bind(this)} />
</CardBody>
</div>
);
}
JSTable.js
import React, { Component } from 'react';
import { Card, CardBody, CardHeader, Col, Row,Button } from 'reactstrap';
import '../../css/jquery.dataTables.css';
import '../../css/dataTables.responsive.css';
const $ = require('jquery');
var ReactDOM = require('react-dom');
$.Datatable = require('datatables.net-responsive');
class JSTable extends Component {
constructor(props) {
super(props);
this.state = {
//ajaxurl : props.ajaxurl,
id : props.id,
dataSet : props.dataSet,
columns : props.columns,
onEdit : props.onEdit,
onDelete: props.onDelete,
onTopUp : props.onTopUp,
render : {
edit :props.onEdit==null?{display:'none'}:{display:''},
delete :props.onDelete==null?{display:'none'}:{display:''},
topup :props.onTopUp==null?{display:'none'}:{display:''},
},
indicator:{
edit :props.onEdit==null?true:false,
delete :props.onDelete==null?true:false,
topup :props.onTopUp==null?true:false,
}
};
}
componentDidMount() {
this.$el = $(this.el);
this.$el.DataTable({
// ajax: this.state.ajaxurl,
data: this.state.dataSet,
columns: this.state.columns,
responsive: true,
columnDefs: [{
targets: 0,
createdCell: (td, cellData, rowData, row, col) =>
ReactDOM.render(
<div>
<Button color="primary" style={this.state.render.edit}
onClick={this.state.indicator.edit?this.empty:this.state.onEdit.bind(this.celldata,this,rowData,this.row,this.col)}
className="mr-1">Edit</Button>
<Button color="primary" style={this.state.render.delete}
onClick={this.state.indicator.delete?this.empty:this.state.onDelete.bind(this.celldata,this,rowData,this.row,this.col)}
className="mr-1">Delete</Button>
<Button color="primary" style={this.state.render.topup}
onClick={this.state.indicator.topup?this.empty:this.state.onTopUp.bind(this.celldata,this,rowData,this.row,this.col)}
className="mr-1">Top Up</Button>
</div>, td
),
}
],
});
}
// componentWillUnmount() {
// this.$el.DataTable.destroy(true);
// }
empty(){
console.log('===========no function=================');
}
render() {
return (
<div className="animated fadeIn">
<table data={this.state.dataSet} className="display" width="100%" ref={el => this.el = el}>
</table>
</div>
);
}
}
export default JSTable;
I can get the update data. However, it doesn't update the table. Unless I refresh the page.
Please advise.
The simplest way to refresh or rerender component is a key.
Your key can be your value in your state
state={counter:null}
Then when data is loaded from your API
Use setState and increment your counter
<table key={this.state.counter}>
//your code
</table>
React Current Image in Image Gallery
You can use this.forceUpdate() for rerender in reactJs.
I'm calling an api and setting the state with the response.
I'm not able to call setState without seeing this error:
The error does not occur if setState happens outside the promise.
How do you set an API response to state without seeing this error?
Can't call setState (or forceUpdate) on an unmounted component
componentDidMount() {
const url = 'https://localhost:8000/api/items'
let items;
fetch(url, {mode: 'cors'})
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
Entire component for reference:
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import GridList from '#material-ui/core/GridList';
import GridListTile from '#material-ui/core/GridListTile';
import GridListTileBar from '#material-ui/core/GridListTileBar';
import ListSubheader from '#material-ui/core/ListSubheader';
import IconButton from '#material-ui/core/IconButton';
import InfoIcon from '#material-ui/icons/Info';
const styles = theme => ({
root: {
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'space-around',
overflow: 'hidden',
backgroundColor: theme.palette.background.paper,
},
icon: {
color: 'rgba(255, 255, 255, 0.54)',
},
});
class ItemList extends Component {
constructor(props) {
super(props)
this.state = {
items: [],
isLoaded: false,
}
this.handleItemClick = this.handleItemClick.bind(this);
}
componentDidMount() {
const url = 'https://localhost:8000/api/items'
let items;
fetch(url, {mode: 'cors'})
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
handleItemClick(id) {
}
handleRenderItems() {
const { classes } = this.props
const { items } = this.state;
return this.state.items.map((item, idx) => {
const id = item.id;
return (
<GridListTile onClick={() => this.handleItemClick(id)} key={idx}>
<img src={item.key_image} alt={item.title} />
<GridListTileBar
title={item.title}
subtitle={<span>${item.rent_price_day}/day</span>}
actionIcon={
<IconButton className={classes.icon}>
<InfoIcon />
</IconButton>
}
/>
</GridListTile>
)
})
}
render() {
const { classes } = this.props;
return (
<div className={classes.root}>
<GridList cols={3} cellHeight={220} className={classes.gridList}>
<GridListTile key="Subheader" cols={3} style={{ height: 'auto' }}>
<ListSubheader component="div">Popular Items</ListSubheader>
</GridListTile>
{this.handleRenderItems()}
</GridList>
</div>
);
}
}
ItemList.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(ItemList);
Instead of handling this in the component - I think it may be simpler to handle the call to the API in an action creator, and use redux to store the data - then just send it to the component when it's available. Better to keep these details out of the view anyway.
Also, wouldn't typically use a style object in the view either - but it's boilerplate with material-ui.
Add below inside your component,
componentDidMount() {
this.isMounted = true;
}
componentWillUnmount() {
this.isMounted = false;
}
Then setState only if this.isMounted is true. This should solve your problem, but this is not a recommended pattern. Please refer https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html
Check the code of parent component which holds the <ItemList/>. If you are rendering <ItemList/> based on certain conditions, the problem might be in those conditions.
It simple. You just have to return the value of the second promise in a variable.
Like this :
let items;
let errorFetch;
fetch(url, {mode: 'cors'})
.then(res => res.json())
.then(
(result) => {
items = result;
},
(error) => {
errorFetch = error
}
);
this.setState(items ? { isLoaded : true, items } : { isLoaded : true, error: errorFetch });