I´m making a call to my API to get a specific object using its id.
As I call console.log in the mapStateToProps method, the entire object is printed, but the state's "anuncio" property is undefined when I try to access it with this.props.anuncio. What am I doing wrong?
Below is how I'm using the reducer and the action.
import React from 'react'
import PropTypes from 'prop-types'
import { fetchAnuncio } from '../../actions/anuncios';
import { connect } from 'react-redux';
import Avatar from 'react-avatar';
import star from '../../imgs/star.png';
class AnuncioDetalhes extends React.Component {
constructor(props) {
super(props);
this.state = {
id: this.props.anuncio ? this.props.anuncio.id : null,
img: this.props.anuncio ? this.props.anuncio.img : null
}
}
componentDidMount() {
if (this.props.match.params.id) {
this.props.fetchAnuncio(this.props.match.params.id);
}
}
render () {
const {id, img} = this.state;
return (
<div>
</div>
);
}
}
AnuncioDetalhes.propTypes = {
fetchAnuncio: PropTypes.func.isRequired,
history: PropTypes.object.isRequired
}
function mapStateToProps(state, props) {
if(props.match.params.id) {
console.log(state.anuncios.find(item => item.id === 4))
return {
anuncio: state.anuncios.find(item => item.id === props.match.params.id)
}
}
return { anuncio: null };
}
export default connect(mapStateToProps, {fetchAnuncio})(AnuncioDetalhes);
reducer:
import { SET_ANUNCIOS, ANUNCIO_FETCHED } from '../actions/types';
export default function anuncios(state = [], action = {}) {
switch(action.type) {
case ANUNCIO_FETCHED:
const index = state.findIndex(item => item.id === action.anuncio.id);
if(index > -1) {
return state.map(item => {
if (item.id === action.anuncio.id) return action.anuncio;
return item;
});
} else {
return [
...state,
action.anuncio
];
}
case SET_ANUNCIOS:
return action.anuncios;
default: return state;
}
}
action:
import axios from 'axios';
import Constants from '../components/Constants'
import { SET_ANUNCIOS, ANUNCIO_FETCHED } from './types';
export function setAnuncios(anuncios) {
return {
type: SET_ANUNCIOS,
anuncios
}
}
export function anuncioFetched(anuncio) {
return {
type: ANUNCIO_FETCHED,
anuncio
};
}
export function fetchAnuncios(cidade) {
return dispatch => {
return axios.get(Constants.BASE_URL + "anuncios/?user__cidade=" + cidade + "&status=2").then(res => {
dispatch(setAnuncios(res.data.results));
});
}
}
export function fetchAnuncio(id) {
return dispatch => {
return axios.get(Constants.BASE_URL + "anuncios/" +`${id}`).then(res => {
dispatch(anuncioFetched(res.data));
});
}
}
I think you should use componentWillReceiveProps lifecycle method to get your updated props.
componentWillReceiveProps = (nextProps) => {
console.log(nextProps)
}
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
How to write the unit test case for the given container which is associated with the component given as below?
Here the PopupNotification.jsx is a component file which has PopupNotification.js as a container file.
I want a jest unit test case implementation for the container file.
PopupNotification.jsx --> component file
import React from "react";
import PropTypes from "prop-types";
import { notyIcons, notyTimeout } from "../helpers/constants";
class PopupNotification extends React.Component {
constructor(props) {
super(props);
this.state = {
showNoty: props.showNoty
};
this.notyTimer = null;
}
static getDerivedStateFromProps(nextProps, prevState) {
return {
showNoty: nextProps.showNoty
};
}
shouldComponentUpdate() {
return true;
}
startNotyTimer() {
let __self = this;
if (this.notyTimer) {
clearTimeout(this.notyTimer);
}
this.notyTimer = setTimeout(function() {
__self.closeNoty();
}, notyTimeout);
}
componentWillUnmount() {
if (this.notyTimer) {
clearTimeout(this.notyTimer);
}
this.setState({ showNoty: false });
}
closeNoty() {
let { id } = this.props;
this.props.clearNotification(id);
this.setState({
showNoty: false
});
}
getNotyIcon() {
return <i className={`notification-popup__icon pos-l-m ${notyIcons[this.props.type]}`} />;
}
getNotyLayout() {
let notyIcon = this.getNotyIcon();
let { message: notyMessage, type } = this.props;
return (
<div className={`pure-g notification-popup ${type}`}>
<div className="pure-u-1-8 pos">{notyIcon}</div>
<div className="pure-u-7-8">
<div className="u-margin-8--left">{notyMessage}</div>
</div>
</div>
);
}
render() {
var notyLayout = null;
if (this.state.showNoty) {
this.startNotyTimer();
notyLayout = this.getNotyLayout();
}
return notyLayout;
}
}
PopupNotification.propTypes = {
message: PropTypes.string,
type: PropTypes.string,
id: PropTypes.number,
showNoty: PropTypes.bool,
clearNotification: PropTypes.func
};
PopupNotification.defaultProps = {
message: "",
type: ""
};
export default PopupNotification;
PopupNotification.js --> container File
import { connect } from "react-redux";
import { actions } from "../ducks/common";
import PopupNotification from "../components/PopupNotification";
const mapStateToProps = state => {
let { common: { popupNotifications = [] } } = state;
let showNoty = false;
if (popupNotifications.length) {
showNoty = true;
}
return {
showNoty,
...popupNotifications[popupNotifications.length-1]
};
};
const mapDispatchToProps = dispatch => {
return {
clearNotification: notyId => {
dispatch(actions.clearPopupNotification({id: notyId}));
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(PopupNotification);
I suggest using redux-mock-store to gain full control over your store. Then you can do things like
const actions = store.getActions()
expect(actions).toEqual([expectedPayload])
Copied from their docs
Since this is a unit test, you just need to test what this container is responsible for, and from what I see from your example that it is exposing the redux connected component, so you should test that your mapStateToProps & mapDispatchToProps are executing correctly.
PROBLEM
I am swapping components based on state in Dashboard(parent) component and passing props to all of them. When I log in using wholesaler the app is running without problems but when i log in with retailer account the app return
TypeError: this.props.handleClick is not a function
when i click on a button-handleClick() -> switch components through handleClick which changes state
My components are almost identical and i have no idea where this is coming from.
Thank you in advance! :)
Files:
Dashboard.js
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import { connect } from 'react-redux'
import { Retailers } from './_components/Retailers'
import { Locations } from './_components/Locations'
import { Products } from './_components/Products'
class Dashboard extends Component {
constructor(props) {
super(props)
this.state = {
chosenRetailerId: null,
chosenLocationId: null,
mountComponent: ''
}
this.handleClick = this.handleClick.bind(this)
}
componentDidMount() {
switch (this.props.user.type) {
case 'wholesaler':
this.setState({ mountComponent: "retailers" })
break;
case 'retailer':
this.setState({ mountComponent: "locations" })
break;
case 'location':
this.setState({ mountComponent: "products" })
break;
default:
break;
}
}
handleClick(id, shouldMountComponent) {
switch (shouldMountComponent) {
case 'locations':
this.setState({
mountComponent: shouldMountComponent,
chosenRetailerId: id
})
break;
case 'products':
this.setState({
mountComponent: shouldMountComponent,
chosenLocationId: id
})
break;
default:
break;
}
}
render() {
const { user } = this.props
const { chosenLocationId, chosenRetailerId, mountComponent } = this.state
return (
<div className="dashboard">
{user.type === 'wholesaler' &&
<div className="wholesaler">
<h1>Wholesaler</h1>
<h3>{user._id}</h3>
{this.state.mountComponent === 'retailers' &&
<Retailers mountComponent={mountComponent} handleClick={this.handleClick} />
}
{this.state.mountComponent === 'locations' &&
<Locations retailerId={chosenRetailerId} locationId={chosenLocationId} mountComponent={mountComponent} handleClick={this.handleClick} />
}
{this.state.mountedComponent === 'products' &&
<Products locationId={chosenLocationId} mountComponent={mountComponent}/>
}
</div>
}
{user.type === 'retailer' &&
<div className="retailers">
<h1>Retailer {user._id}</h1>
<Locations locationId={chosenLocationId}/>
</div>
}
{user.type === 'location' &&
<div className="locations">
<h1>Location {user._id}</h1>
<Products />
</div>
}
<p>You're logged in with React & JWT!!</p>
<p>
<Link to="/login">Logout</Link>
</p>
</div>
)
}
}
function mapStateToProps(state) {
const { authentication } = state
const { user } = authentication
return {
user
}
}
const connectedDashboard = connect(mapStateToProps)(Dashboard)
export { connectedDashboard as Dashboard }
Retailers.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
class Retailers extends Component {
state = {
retailers: []
}
componentDidMount() {
const { user } = this.props
const requestOptions = {
method: 'GET',
headers: { 'Authorization': 'Bearer ' + user.token }
}
fetch(`http://localhost:4000/retailers/by-wholesaler/${user._id}`, requestOptions)
.then(result => result.json())
.then(result => {
this.setState({
retailers: result
})
})
}
render() {
const { retailers } = this.state
return (
<div className="retailers">
{retailers.map((retailer, index) =>
<button key={index} onClick={() => this.props.handleClick(retailer._id, 'locations')}>{retailer.name}</button>
)}
</div>
)
}
}
function mapStateToProps(state) {
const { authentication } = state
const { user } = authentication
return { user }
}
const connectedRetailers = connect(mapStateToProps)(Retailers)
export { connectedRetailers as Retailers }
Locations.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
class Locations extends Component {
state = {
locations: []
}
componentDidMount() {
const { user } = this.props
const requestOptions = {
method: 'GET',
headers: { 'Authorization': 'Bearer ' + user.token }
}
const retailerId = (user.type === 'retailer') ? user._id : this.props.retailerId
console.log(retailerId)
fetch(`http://localhost:4000/locations/by-retailer/${retailerId}`, requestOptions)
.then(result => result.json())
.then(result => {
this.setState({
locations: result
})
})
}
render() {
const { locations } = this.state
return (
<div className="locations">
{locations.map((location, index) =>
<button key={index} onClick={() => this.props.handleClick(location._id, 'products')}>{location.name}</button>
)}
</div>
)
}
}
function mapStateToProps(state) {
const { authentication } = state
const { user } = authentication
return { user }
}
const connectedLocations = connect(mapStateToProps)(Locations)
export { connectedLocations as Locations }
You have to pass handleCLick to location as a prop. You do that in you wholesaler case (passing it to the retailer component), but not when using the Locations component
You didn't pass handleClick as a prop :)
<Retailers handleClick={this.handleClick} />
<Locations handleClick={this.handleClick} />
So prop is undefined and you can't call it as a function.
Check the locations.map function in your Locations.js.
Your are passing a so called thisArg to the map function, so it is no longer using the right context.
This should work:
<div className="locations">
{locations.map((location, index) =>
<button key={index} onClick={() =>
this.props.handleClick(location._id, 'products')}>
{location.name}
</button>
)}
</div>
Also, think about using the uuid package for your iteration keys. Now you are using an index and that will not be unique if you do so in another iteration too (not the case yet so).
I am creating a basic todo application in react with flux. I am trying to make it so that every time a new todo is entered in the API, the list is update in real time rather than me having to reload the page the and application making another request. I have tried using componentDidUpdate; however that doesn't seem to work in this case. How should I go about implementing that?
My Todos.js
import React from "react";
import * as TodoActions from "../actions/TodoActions";
import TodoStore from "../stores/TodoStore";
import './Todos.css'
export default class Todos extends React.Component {
constructor() {
super();
this.addItem = this.addItem.bind(this);
this.deleteTodo = this.deleteTodo.bind(this)
this.getTodos = this.getTodos.bind(this);
this.state = {
todos: TodoStore.getAll(),
};
TodoActions.receiveTodos()
}
componentWillMount() {
TodoStore.addChangeListener(this.getTodos);
}
componentWillUnmount() {
TodoStore.removeChangeListener(this.getTodos);
}
componentDidMount() {
TodoActions.receiveTodos();
}
componentDidUpdate() {
TodoActions.receiveTodos();
}
getTodos() {
this.setState({
todos: TodoStore.getAll(),
});
}
deleteTodo(id) {
TodoActions.deleteTodo(id);
}
addItem(e) {
e.preventDefault();
TodoActions.createTodo(this._inputElement.value)
}
render() {
const { todos } = this.state;
const TodoComponents = todos.map((todo) => {
return (
<div key={todo.id} className="todo-list">
<div className="todo-name">{todo.name}</div>
<div className="todo-btn"><button type="button" onClick={() => this.deleteTodo(todo.id)}>Delete</button></div>
</div>
)
});
return (
<div className="main-container">
<h1 className="title">Todos</h1>
<ul>{TodoComponents}</ul>
<form onSubmit={this.addItem}>
<input ref={(a) => this._inputElement = a} placeholder="Enter Task" className="input-form"/>
<button type="submit" className="input-btn">Add</button>
</form>
</div>
);
}
}
My TodoStore.js
import { EventEmitter } from "events";
import AppDispatcher from "../dispatcher";
let _todos = []
function setTodos(todos) {
_todos = todos;
}
class TodoStore extends EventEmitter {
emitChange = () => {
this.emit("change");
}
addChangeListener = (callback) => {
this.on("change", callback)
}
removeChangeListener = (callback) => {
this.removeListener("change", callback)
}
getAll = () => {
return _todos;
}
handleActions = (action) => {
switch(action.type) {
case "RECEIVE_TODOS": {
setTodos(action.todos);
this.emit("change")
break;
}
}
}
}
const todoStore = new TodoStore;
AppDispatcher.register(todoStore.handleActions.bind(todoStore));
export default todoStore;
My TodoActions.js
import AppDispatcher from "../dispatcher";
import apiClient from "../api-client"
export function createTodo(name) {
apiClient.createTodo({name: name}).then((result) => {
AppDispatcher.dispatch({
type: "CREATE_TODO",
name: result,
});
})
}
export function deleteTodo(id) {
apiClient.deleteTodo(id).then((result) => {
AppDispatcher.dispatch({
type: "DELETE_TODO",
id,
});
})
}
export function receiveTodos() {
apiClient.getAllTodos().then(result => {
AppDispatcher.dispatch({
type: "RECEIVE_TODOS",
todos: result.payload
});
})
}
i have a async fetch that gets values like A001, A002, names and so on from an API. But sometimes i get 3 or more values with 1 api fetch and i loose some because i show only 3 values at a time in my react component.
So i look for a way to continuously show 1 entry from the store for like 2 seconds and then the next entry and so on.
Can someone please help me here?
Actions
let lastId = 0;
let pathArray = window.location.pathname.split('/');
export const fetchLastId = () => dispatch => {
const url = '/api/display/sites/' + pathArray[3] + '/displays/' + pathArray[5] + '/show';
fetch(url, {
method: 'GET',
mode: 'cors',
headers: {
'Authorization': ''
},
'Content-Type': 'application/json'
}).then(function(response) {
return response.json();
}).then(function(data) {
if (data.length) {
lastId = data[0].id;
} else {
lastId = 0;
}
console.log('Die Letzte ID war ' + lastId);
}).catch(function(error) {
console.log('Fehler: ', error);
})
}
export const fetchLastCalls = () => dispatch => {
const url = '/api/display/sites/' + pathArray[3] + '/displays/' + pathArray[5] + '/calls?id_greater_than=' + lastId;
fetch(url, {
method: 'GET',
mode: 'cors',
headers: {
'Authorization': ''
},
'Content-Type': 'application/json'
}).then(function(response) {
return response.json();
}).then(function(data) {
data.reverse();
if (data.length) {
for (let item of data) {
switch (item.service_id) {
case 24:
dispatch({ type: 'SERVICE_1', payload: item })
break;
case 25:
dispatch({ type: 'SERVICE_2', payload: item })
break;
default:
console.log('Aufruf im Falschen Dienst getätigt.')
}
lastId = item.id;
}
} else {
console.log('Keine neuen Aufrufe.');
}
}).catch(function(error) {
console.log('Fehler: ', error);
})
}
Reducer
let initialState = [];
function service1(state = initialState, action) {
if (action.type === 'SERVICE_1') {
return [action.payload, ...state];
}
return state;
}
export default service1;
Container
import React from 'react';
import { connect } from 'react-redux';
import { NewCall } from '../components/NewCall';
import { LastCall } from '../components/LastCall';
import { fetchLastId , fetchLastCalls } from '../actions/index';
class Service1 extends React.Component {
componentWillMount() {
this.props.onFetchLastId();
}
componentDidMount() {
setInterval(function() {
this.props.onFetchLastCalls();
}.bind(this), 1000);
}
renderNewTicket() {
return this.props.calls.map(call => {
return (
<p key={call.ticket}>{call.ticket}</p>
);
});
}
renderNewPlace() {
return this.props.calls.map(call => {
return (
<p key={call.desk_id}>{call.desk_id}</p>
);
});
}
renderLastTicket() {
return this.props.calls.map(call => {
return (
<p key={call.ticket}>{call.ticket}</p>
)
})
}
renderLastPlace() {
return this.props.calls.map(call => {
return (
<p key={call.desk_id}>{call.desk_id}</p>
)
})
}
componentDidUpdate() {
}
render() {
return(
<div>
<NewCall
call={ this.renderNewTicket() }
place={ this.renderNewPlace() }
/>
<LastCall
call={ this.renderLastTicket() }
place={ this.renderLastPlace() }
rollOn={1}
/>
<LastCall
call={ this.renderLastTicket() }
place={ this.renderLastPlace() }
rollOn={2}
/>
</div>
);
}
}
function mapStateToProps(state) {
return {
calls: state.service1
};
}
let mapDispatchToProps = {
onFetchLastId: fetchLastId,
onFetchLastCalls: fetchLastCalls
}
export default connect(mapStateToProps, mapDispatchToProps)(Service1);
1 Output Component
import React from 'react';
import { Textfit } from 'react-textfit';
import Blink from './Blink';
const inlineStyle = {
width: 945,
height: 249
};
export class NewCall extends React.Component {
render() {
return(
<div>
<div className="flex-item-grey ticketNrGr">
<Textfit mode="multi" style={inlineStyle} className="textfit" max={200}><Blink>{this.props.call[0]}</Blink></Textfit>
</div>
<div className="flex-item-grey platzNrGr">
<Textfit mode="multi" style={inlineStyle} className="textfit" max={200}><Blink>{this.props.place[0]}</Blink></Textfit>
</div>
</div>
);
}
}
Second Output Component
import React from 'react';
import { Textfit } from 'react-textfit';
const inlineStyleCall = {
width: 735,
height: 195
};
const inlineStyleDesk = {
width: 200,
height: 195
};
export class LastCall extends React.Component {
render() {
return(
<div className="flex-container-aufrufKl">
<div className="flex-item-grey ticketNrKl">
<Textfit mode="multi" style={inlineStyleCall} className="textfit" max={200}>{this.props.call[this.props.rollOn]}</Textfit>
</div>
<div className="flex-item-grey platzNrKl">
<Textfit mode="multi" style={inlineStyleDesk} className="textfit" max={200}>{this.props.place[this.props.rollOn]}</Textfit>
</div>
</div>
);
}
}
It sounds to me like it should be the responsibility of the component to trigger a new action to render the next available value. So you could have a component that accepts the value value and a boolean indicating if there are any more values: hasMoreValues.
In your react component, you'll need to make it a class to access the lifecycle hooks and hook into the componentDidMount function:
class MyComponent extends Component {
constructor(props) {
super(props)
}
componentDidMount() {
if(this.props.hasMoreValues) {
setTimeout(this.props.showNextValue, 2000)
}
}
render() {
return (
<div>{this.props.value}</div>
)
}
}
export default connect(
(props) => ({
hasMoreValues: getHasMoreValues(),
value: getNextValue(),
)},
{
showNextValue: () => { type: 'SHOW_NEXT_ACTION' },
},
)
An approach like this means that the trigger is still coming from the application. The next thing to do is update the item to view to be the next one available in your reducer, but I'll leave that up to you.