How to stop react component from infinite re-render - reactjs

I wanna use js-cookie in my app, but the time I get the cookie my component keeps re-rendering until the browser crash.
The error I get is: setState(...): Cannot update during an existing state transition ...
I've just used the shouldComponentUpdate but it caused the click events not working.
shouldComponentUpdate(nextProps, nextState)
{
return nextState.language != this.state.language;
}
Does anybody know any other solution rather than shouldComponentUpdate to stop a component from infinite re-render?
class MainLayout extends Component {
constructor(props) {
super(props);
console.log('constructor');
this.state = {
sideBarOpen: false,
languages: getStoreLanguages,
language: Cookies.get('langCode')
}
}
componentWillMount() {
this.props.langCode();
this.props.defaultLangCode();
}
componentDidMount() {
$('.dropdown-toggle').megaMenu && $('.dropdown-toggle').megaMenu({ container: '.mmd' });
}
shouldComponentUpdate(nextProps, nextState) {
return nextState.language != this.state.language;
}
toggleSidebar = () => {
this.setState({
sideBarOpen: !this.state.sideBarOpen,
});
}
overlayClickHandler = () => {
this.setState({
sideBarOpen: false,
});
}
handleLanguage = (langCode) => {
if (Cookies.get('langCode')) {
return Cookies.get('langCode');
} else {
Cookies.set('langCode', langCode, { expires: 7 });
return langCode;
}
}
render() {
let overlay = { display: this.state.sideBarOpen ? 'block' : 'none' };
const langCode = this.handleLanguage(this.props.params.id);
const isDefaultLang = isDefaultLanguage(langCode);
const isValidLang = isValidLanguage(langCode);
if (langCode && !isValidLang) {
this.props.router.push(`/${langCode}/error`);
}
if (langCode && isValidLang) {
const path = getLocalisedPath(langCode, isDefaultLang)
this.props.router.push("/" + path);
}
return (
<div>
<Helmet>
<script type="application/ld+json">{structuredData()}</script>
</Helmet>
<TokenManager>
{(state, methods) => (
<div>
<WithHelmet {...this.props} />
<WithUserHeaderInfo {...this.props} />
<WithStoreDetail />
<WithDataLayerStoreDetail />
<header className='header__nav'>
<NavbarManagerWillHideOnEditor
sideBarOpen={this.state.sideBarOpen}
toggleSidebar={this.toggleSidebar}
languages={this.state.languages}
{...this.props}
/>
<div
className="mmm__overlay"
onClick={this.overlayClickHandler}
style={overlay}
/>
<div
className="mmm__overlay--hidenav"
onClick={this.overlayClickHandler}
style={overlay}
/>
</header>
<main>{this.props.children}</main>
<Modal modalId="modal-account" size="md">
{(closeModal) => (
<Auth
closeModal={closeModal} />
)}
</Modal>
{!this.props.location.pathname.startsWith('/checkout') && <FooterWillHideOnEditor languages={this.state.languages}/>}
</div>
)
}
</TokenManager>
</div>
);
}
}
const mapDispatchToProps = (dispatch, value) => {
const langCode = Cookies.get('langCode') || value.params.id;
const defaultLang = getDefaultLanguage();
const isDefault = isDefaultLanguage(langCode);
const isValid = isValidLanguage(langCode);
const lang = !isValid || isDefault ? defaultLang : langCode;
return {
langCode: () => dispatch({ type: 'SET_LANGUAGE', payload: lang }),
defaultLangCode: () => dispatch({ type: 'SET_DEFAULT_LANGUAGE', payload: defaultLang })
}
}
export default connect(null, mapDispatchToProps)(MainLayout);

let overlay = { display: this.state.sideBarOpen ? 'block' : 'none' };
const langCode = this.handleLanguage(this.props.params.id);
const isDefaultLang = isDefaultLanguage(langCode);
const isValidLang = isValidLanguage(langCode);
if (langCode && !isValidLang) {
this.props.router.push(`/${langCode}/error`);
}
if (langCode && isValidLang) {
const path = getLocalisedPath(langCode, isDefaultLang)
this.props.router.push("/" + path);
}
Here the code is resetting the state/ or mapDispatchToProps dispatched again .that's why it's rerendering.

I found the reason why component keep re-rendering, actually it was because of the condition in:
if (langCode && isValidLang) {
const path = getLocalisedPath(langCode, isDefaultLang)
this.props.router.push("/" + path);
}
which is always true and gets the path and push to the route which cause the component re-render.
Thanks

Related

Unable to get last update state from new window in react

I have four js file to demonstrate my problem.
home.js
command-input.js
tree-view.js
new-window.js
I want to open a new window from command-input.js file along with the command text. From tree-view.js file it works fine but not from command-input.js file.
In my program I have changed the state based on my tree node name and then call window opener method from child window to get the node name. It works fine but dose not work from command-input.js file(can't get the last command text).
Home.js
let browser = window;
let newWindow = null;
class Home extends React.PureComponent {
constructor(props) {
super(props);
this.state = { SBFCommand: {} };
browser = window.self;
browser.getCompName = () => {
return this.state.SBFCommand;
};
}
openNewWindow =(commandText)=> {
this.setState({
SBFCommand: {
ComponentName: compName,
SBCommand: comndText,
componentTitle: complabel,
},
});
newWindow = browser.open(
"/sbfrontapp/sb-user-component",
Math.random(),
"width=900,height=600,toolbar=no,scrollbars=no,location=no,resizable =no"
);
}
}
command-input.js
class CommanInput extends Component {
keyDownHandler = (event) => {
if (event.key === "Enter") {
if (event.target.value !== "") {
let cmdText = event.target.value.toUpperCase();
this.child.openNewWindow(cmdText);
}
}
};
render() {
return (
<div>
<input
type="text"
className="sb-magic-box"
onKeyDown={(e) => this.keyDownHandler(e)}
/>
<Home onRef={(ref) => (this.child = ref)} />
</div>
);
}
}
tree-view.js
class TreeView extends PureComponent{
onNodeClick = (node) => {
if (node.isLeaf) {
const cmdText = node.value.split("_")[1].toUpperCase();
if (cmdText !== null && cmdText !== "") {
this.child.openNewWindow(cmdText);
}
}
};
render() {
const { expanded, nodeData, loading } = this.state;
return (
<>
<Home onRef={(ref) => (this.child = ref)} />
{loading ? (
<LoadImg />
) : (
<CheckboxTree
expanded={expanded}
iconsClass="fa5"
nodes={nodeData}
onClick={this.onNodeClick}
onExpand={this.onExpand}
expandOnClick="true"
/>
)}
</>
);
}
}
new-window.js
class NewWindow extends React.Component {
state = {
loading: false,
ComponentName: "",
SBCommand: "",
};
componentDidMount() {
if (!window.opener) {
window.close();
}
const cmdDetails = window.opener.getCompName();
window.document.title = cmdDetails.componentTitle;
this.setState({
ComponentName: cmdDetails.ComponentName,
SBCommand: cmdDetails.SBCommand,
});
}
render() {
const { loading, ComponentName, SBCommand } = this.state;
return loading ? (
<LoadImg />
) : (
<React.Suspense fallback={<LoadImg />}>
<div className="sb-document-wrapper">
{SBCommand}
</div>
</React.Suspense>
);
}
}

React: triggering method inside HOC component

What I want to do, is create a HOC that has a method that can be triggered by whatever Parent Component is using that HOC to wrap.
For this HOC, I'm trying to fade out the HOC and any components inside it:
HOC:
export function fadeOutWrapper(WrappedComponent) {
return class extends Component {
constructor(props) {
super(props);
this.state = {
showElement: true,
removeElement: false,
};
}
_triggerFade = () => {
this._fadeOut(this.props.time).then(time => this._removeElement(time));
}
_fadeOut = time => {
let _this = this;
return new Promise((resolve, reject) => {
_this.setState({
showElement: false
});
setTimeout(() => {
resolve(time);
}, time);
});
};
_removeElement = time => {
let _this = this;
setTimeout(() => {
_this.setState({
removeElement: true
});
}, time + 500);
};
render() {
return this.state.removeElement ? null : (
<div
className={
this.state.showElement
? "cfd-container"
: "cfd-container cfd-fadeout"
}
>
<WrappedComponent {...this.props} />
</div>
);
}
};
}
How this component is being used in parent component:
import ComponentToBeFaded from '...';
import { fadeOutWrapper } from '...';
const WrappedComponent = fadeOutWrapper(ComponentToBeFaded);
class ParentComponent extends Component {
const...
super...
handleChildClick = () => {
// ? how to trigger the HOC _triggerFade method?
// WrappedComponent._triggerFade()
}
render() {
return (
<WrappedComponent time={1000} handleClick={this.handleChildClick} {...other props component needs} />
)
}
}
What I want to be able to do is call a method that is inside the HOC, can't seem to check for a change in props inside the HOC... only inside the HOC's render()
Need to keep writing more to meet the submission quota. Any thoughts on how to do this is appreciated. Hope your day is going well!
You don't need showElement in local state of the wrapped component because it's not controlled by that component. Pass it as props and use componentDidUpdate to start fading out.
const { Component, useState, useCallback } = React;
const Button = ({ onClick }) => (
<button onClick={onClick}>Remove</button>
);
function App() {
const [show, setShow] = useState(true);
const onClick = useCallback(() => setShow(s => !s), []);
return (
<WrappedButton
time={1000}
onClick={onClick}
showElement={show}
/>
);
}
function fadeOutWrapper(WrappedComponent) {
return class extends Component {
constructor(props) {
super(props);
this.state = {
removeElement: false,
fadeout: false,
};
}
componentDidUpdate(prevProps) {
if (
this.props.showElement !== prevProps.showElement &&
!this.props.showElement
) {
this._triggerFade();
}
}
_triggerFade = () => {
this._fadeOut(this.props.time).then(() =>
this._removeElement()
);
};
_fadeOut = time => {
this.setState({ fadeout: true });
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, time);
});
};
_removeElement = time => {
this.setState({
removeElement: true,
});
};
render() {
return this.state.removeElement ? null : (
<div>
{JSON.stringify(this.state)}
<WrappedComponent {...this.props} />
</div>
);
}
};
}
const WrappedButton = fadeOutWrapper(Button);
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Firebase/React/Redux Component has weird updating behavior, state should be ok

I am having a chat web app which is connected to firebase.
When I refresh the page the lastMessage is loaded (as the gif shows), however, for some reason, if the component is otherwise mounted the lastMessage sometimes flickers and disappears afterwards like it is overridden. When I hover over it, and hence update the component, the lastMessage is there.
This is a weird behavior and I spent now days trying different things.
I would be very grateful if someone could take a look as I am really stuck here.
The db setup is that on firestore the chat collection has a sub-collection messages.
App.js
// render property doesn't re-mount the MainContainer on navigation
const MainRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props => (
<MainContainer>
<Component {...props} />
</MainContainer>
)}
/>
);
render() {
return (
...
<MainRoute
path="/chats/one_to_one"
exact
component={OneToOneChatContainer}
/>
// on refresh the firebase user info is retrieved again
class MainContainer extends Component {
componentDidMount() {
const { user, getUserInfo, firebaseAuthRefresh } = this.props;
const { isAuthenticated } = user;
if (isAuthenticated) {
getUserInfo(user.id);
firebaseAuthRefresh();
} else {
history.push("/sign_in");
}
}
render() {
return (
<div>
<Navigation {...this.props} />
<Main {...this.props} />
</div>
);
}
}
Action
// if I set a timeout around fetchResidentsForChat this delay will make the lastMessage appear...so I must have screwed up the state / updating somewhere.
const firebaseAuthRefresh = () => dispatch => {
firebaseApp.auth().onAuthStateChanged(user => {
if (user) {
localStorage.setItem("firebaseUid", user.uid);
dispatch(setFirebaseAuthUser({uid: user.uid, email: user.email}))
dispatch(fetchAllFirebaseData(user.projectId));
}
});
};
export const fetchAllFirebaseData = projectId => dispatch => {
const userId = localStorage.getItem("firebaseId");
if (userId) {
dispatch(fetchOneToOneChat(userId));
}
if (projectId) {
// setTimeout(() => {
dispatch(fetchResidentsForChat(projectId));
// }, 100);
...
export const fetchOneToOneChat = userId => dispatch => {
dispatch(requestOneToOneChat());
database
.collection("chat")
.where("userId", "==", userId)
.orderBy("updated_at", "desc")
.onSnapshot(querySnapshot => {
let oneToOne = [];
querySnapshot.forEach(doc => {
let messages = [];
doc.ref
.collection("messages")
.orderBy("created_at")
.onSnapshot(snapshot => {
snapshot.forEach(message => {
messages.push({ id: message.id, ...message.data() });
});
});
oneToOne.push(Object.assign({}, doc.data(), { messages: messages }));
});
dispatch(fetchOneToOneSuccess(oneToOne));
});
};
Reducer
const initialState = {
residents: [],
oneToOne: []
};
function firebaseChat(state = initialState, action) {
switch (action.type) {
case FETCH_RESIDENT_SUCCESS:
return {
...state,
residents: action.payload,
isLoading: false
};
case FETCH_ONE_TO_ONE_CHAT_SUCCESS:
return {
...state,
oneToOne: action.payload,
isLoading: false
};
...
Main.js
// ...
render() {
return (...
<div>{React.cloneElement(children, this.props)}</div>
)
}
OneToOne Chat Container
// without firebaseAuthRefresh I don't get any chat displayed. Actually I thought having it inside MainContainer would be sufficient and subscribe here only to the chat data with fetchOneToOneChat.
// Maybe someone has a better idea or point me in another direction.
class OneToOneChatContainer extends Component {
componentDidMount() {
const { firebaseAuthRefresh, firebaseData, fetchOneToOneChat } = this.props;
const { user } = firebaseData;
firebaseAuthRefresh();
fetchOneToOneChat(user.id || localStorage.getItem("firebaseId"));
}
render() {
return (
<OneToOneChat {...this.props} />
);
}
}
export default class OneToOneChat extends Component {
render() {
<MessageNavigation
firebaseChat={firebaseChat}
firebaseData={firebaseData}
residents={firebaseChat.residents}
onClick={this.selectUser}
selectedUserId={selectedUser && selectedUser.residentId}
/>
}
}
export default class MessageNavigation extends Component {
render() {
const {
onClick,
selectedUserId,
firebaseChat,
firebaseData
} = this.props;
<RenderResidentsChatNavigation
searchChat={this.searchChat}
residents={residents}
onClick={onClick}
firebaseData={firebaseData}
firebaseChat={firebaseChat}
selectedUserId={selectedUserId}
/>
}
}
const RenderResidentsChatNavigation = ({
residents,
searchChat,
selectedUserId,
onClick,
firebaseData,
firebaseChat
}) => (
<div>
{firebaseChat.oneToOne.map(chat => {
const user = residents.find(
resident => chat.residentId === resident.residentId
);
const selected = selectedUserId == chat.residentId;
if (!!user) {
return (
<MessageNavigationItem
id={chat.residentId}
key={chat.residentId}
chat={chat}
onClick={onClick}
selected={selected}
user={user}
firebaseData={firebaseData}
/>
);
}
})}
{residents.map(user => {
const selected = selectedUserId == user.residentId;
const chat = firebaseChat.oneToOne.find(
chat => chat.residentId === user.residentId
);
if (_isEmpty(chat)) {
return (
<MessageNavigationItem
id={user.residentId}
key={user.residentId}
chat={chat}
onClick={onClick}
selected={selected}
user={user}
firebaseData={firebaseData}
/>
);
}
})}
</div>
}
}
And lastly the item where the lastMessage is actually displayed
export default class MessageNavigationItem extends Component {
render() {
const { hovered } = this.state;
const { user, selected, chat, isGroupChat, group, id } = this.props;
const { messages } = chat;
const item = isGroupChat ? group : user;
const lastMessage = _last(messages);
return (
<div>
{`${user.firstName} (${user.unit})`}
{lastMessage && lastMessage.content}
</div>
)
}
In the end it was an async setup issue.
In the action 'messages' are a sub-collection of the collection 'chats'.
To retrieve them it is an async operation.
When I returned a Promise for the messages of each chat and awaited for it before I run the success dispatch function, the messages are shown as expected.

How do I manage my array of children components' states?

I'm new to react, so forgive me. I'm having a problem understanding states, specifically those of children.
Purpose: I'm trying to create a form that a user can append more and more components -- in this case, images.
What happens: User appends 2 or more images. User tries to upload an image with UploadButton component, but both the images are the same. I believe this has to do with both appended children sharing the same state.
Question: How do I give each appended child its own image without affecting the other appended children?
class Page extends Component
constructor (props) {
super(props);
this.state = {
id: '',
numChildren: 0,
images: [],
}
this.onAddChild = this.onAddChild.bind(this);
}
showModal() {
this.setState({
numChildren: 0,
images: [],
});
}
renderModal()
const children = [];
//Here's my array of child components
for(var i = 0; i < this.state.numChildren; i += 1) {
children.push(<this.ChildComponent key={i} />);
}
return (
<ReactModal>
<this.ParentComponent addChild={this.onAddChild}>
{children}
</this.ParentComponent>
</ReactModal>
)
}
onAddChild = () => {
this.setState({
numChildren: this.state.numChildren + 1
})
}
ParentComponent = (props) => (
<div>
{props.children}
<Button onClick={props.addChild}>Add Item</Button>
</div>
);
ChildComponent = () => (
<div>
<UploadButton
storage="menus"
value={this.state.images}
onUploadComplete={uri => this.setState({images: uri})}
/>
</div>
);
}
Here's the code for UploadButton:
import React, { Component } from 'react';
import uuid from 'uuid';
import firebase from '../config/firebase';
class UploadButton extends Component {
constructor(props) {
super(props);
this.state = {
isUploading: false
}
}
handleClick() {
const input = document.createElement("INPUT");
input.setAttribute("type", "file");
input.setAttribute("accept", "image/gif, image/jpeg, image/png");
input.addEventListener("change", ({target: {files: [file]}}) => this.uploadFile(file));
input.click();
}
uploadFile(file) {
console.log('F', file);
const id = uuid.v4();
this.setState({ isUploading: true })
const metadata = {
contentType: file.type
};
firebase.storage()
.ref('friends')
.child(id)
.put(file, metadata)
.then(({ downloadURL }) => {
this.setState({ isUploading: false })
console.log('Uploaded', downloadURL);
this.props.onUploadComplete(downloadURL);
})
.catch(e => this.setState({ isUploading: false }));
}
render() {
const {
props: {
value,
style = {},
className = "image-upload-button",
},
state: {
isUploading
}
} = this;
return (
<div
onClick={() => this.handleClick()}
className={className}
style={{
...style,
backgroundImage: `url("${this.props.value}")`,
}}>
{isUploading ? "UPLOADING..." : !value ? 'No image' : ''}
</div>
);
}
}
export default UploadButton;
I tried to exclude all unnecessary code not pertaining to my problem, but please, let me know if I need to show more.
EDIT: This is my attempt, it doesn't work:
//altered my children array to include a new prop
renderModal() {
const children = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
children.push(<this.ChildComponent imageSelect={this.onImageSelect} key={i} />);
}
//...
};
//my attempt to assign value and pass selected image back to images array
ChildComponent = () => (
<div>
<UploadButton
storage="menus"
value={uri => this.props.onImageSelect(uri)} //my greenness is really apparent here
onUploadComplete={uri => this.setState({images: uri})}
/>
//...
</div>
);
//added this function to the class
onImageSelect(uri) {
var el = this.state.images.concat(uri);
this.setState({
images: el
})
}
I know I'm not accessing the child prop correctly. This is the most complexity I've dealt with so far. Thanks for your time.
When you write this.state in Child / Parent component, you are actually accessing the state of Page. Now, I would recommend that you pass in the index of the child to the Child like so
children.push(<this.ChildComponent key={i} index={i}/>)
so that each children deals with only its own image like so
ChildComponent = ({index}) => (
<div>
<UploadButton
storage="menus"
value={this.state.images[index]}
onUploadComplete={uri => {
let images = this.state.images.slice()
images[index] = uri
this.setState({images})
}}
/>
</div>
);

React/Redux Why does specific component update, when its sibling’s child component updates, though its state doesn’t change

Update
The sidedrawers state is apparently different, but value does not change...
Details:
There is a layout component, which takes in routes from react router as children.
Inside the layout component code, two child components are rendered, Toolbar, and sidedrawer, and a main section that contains this.props.children.
One of the routes renders a component called page. Page renders another component called graphContainer, and passes it a click event, which is applied to the graphContainer’s button that it renders.
How it works is, I grab the first eight graphs and show 4 of them. When the button is clicked, it decides to either show the next 4 or grab the next eight.
This whole thing uses redux. There’s a page state, authentication state, navigation state, and a graph state. The only partial state changing when the button is clicked, is the graphs.
However, both the GraphContainer updates along with the sidedrawer component. As far as I can tell, nothing in the sidedrawer component is changing, so it should not trigger an update.
In the redux page for navigation state, the switch hits the default, which just returns state.
The graph redux portion works just fine, updates accordingly.
My workaround was to implement a dontUpdate prop in the navigation reducer state. And then use shouldComponentUpdate to check that prop, because the shallow check that was done by default, say with pureComponent, was seeing a different state or prop.
tl;dr: Any ideas why the sidedrawer component keeps updating, even though, as far as I can tell, there’s no prop or state change?
Reducers
const graphReducer = (state = initialState, action) => {
...
case SHOW_NEXTFOUR:
console.log('SHOW NEXT FOUR', state);
return {
...state,
ttlShown: action.ttlShown
};
default:
return state;
}
};
const navReducer = (state = initialState, action) => {
...
default:
return {...state, dontUpdate: true};
}
};
Layout Component
class Layout extends Component {
...
handleSideBarOpen = () => {
this.props.onSidebarToggle();
}
render () {
return (
<Aux>
<Toolbar
isAuth={this.props.isAuthenticated}
drawerToggleClicked={this.handleSideBarOpen}
/>
<SideDrawer
open={this.props.sidebarOpen}
closed={this.props.onSidebarToggle}
/>
<main className={classes.Content}>
{this.props.children}
</main>
</Aux>
)
}
}
const mapStateToProps = ({ navigation, auth }) => {
const { sidebarOpen } = navigation;
const { token } = auth;
return {
sidebarOpen,
isAuthenticated: token !== null
};
};
const mapDispatchToProps = {
onSidebarToggle, getNavTree
};
export default connect(
mapStateToProps, mapDispatchToProps
)(Layout);
Sidedrawer Component
class sideDrawer extends Component {
state = {
popupMenuOpen: false
}
shouldComponentUpdate ( nextProps, nextState ) {
if(nextProps.dontUpdate)
return false;
else return true;
}
…
render() {
…
let navitems = [];
if(this.props.navData && !this.props.error) {
navitems = (
<NavigationItems
showClients={this.props.showClientsBtn}
navData={this.props.navData}
curClientid={this.props.curClientid}
curSiteid={this.props.curSiteid}
curDashid={this.props.curDashid}
curPageid={this.props.curPageid}
closeSidebar={this.props.closed}
onPageClick={this.handlePageClick}
onCSDClick={this.handleOpenPopupMenu}
/>
);
} else
navitems = <p>Problem Loading Tree</p>;
return (
<Aux>
<div className={attachedClasses.join(' ')}>
<div className={classes.Logo}>
<div className={classes.CloseWrapper}>
<Chip onClick={this.props.closed} className={classes.CloseChip}>X</Chip>
</div>
<div className={classes.CrumbWrapper}>
<Breadcrumbs
backBtn={this.handleBackClick}
handleCrumbClick={this.handleCrumbClick}
breadcrumbs={this.props.breadcrumbs}
/>
</div>
</div>
<nav>
{navitems}
<Popover
style={{width: "90%"}}
open={this.state.popupMenuOpen}
anchorEl={this.state.anchorEl}
anchorOrigin={{horizontal: 'middle', vertical: 'bottom'}}
targetOrigin={{horizontal: 'middle', vertical: 'top'}}
onRequestClose={this.handleClosePopupMenu}
>
<Menu
style={{width: "87%"}}>
{MIs}
</Menu>
</Popover>
</nav>
</div>
</Aux>
);
}
};
const mapStateToProps = ({ navigation }) => {
const { dontUpdate, clientid, breadcrumbs,currentPage, selectedClient, selectedSite, selectedDash, selectedPage, navigationData, sidebarOpen, navError } = navigation;
...
}
return {
dontUpdate,
clientid,
showClientsBtn,
navData,
curClientid,
curSiteid,
curDashid,
curPageid,
parentPageid,
sidebarOpen,
navError,
breadcrumbs,
currentPage
};
};
const mapDispatchToProps = {
getNavTree,
onPageSelected,
onSwitchCSD,
onPageRoute
};
export default withRouter(connect(
mapStateToProps, mapDispatchToProps
)(sideDrawer));
Page Component
class Page extends Component {
componentWillMount () {
this.props.getCurPage();
}
render () {
let content = null;
if(this.props.location.state && this.props.location.state.currentPage)
content = (<GraphContainer pageid={this.props.location.state.currentPage} />);
return this.props.location.state && this.props.location.state.currentPage ? (
<Aux>
<p>A PAGE!</p>
{content}
</Aux>
) : (<Redirect to="/" />);
}
}
const mapStateToProps = ({pages}) => {
const { clientid, curPage } = pages;
return {
clientid, curPage
};
};
const mapDispatchToProps = {
getSelectedPage, getCurPage
};
export default connect(
mapStateToProps, mapDispatchToProps
)(Page);
Graph Container
class GraphsContainer extends Component {
componentWillReceiveProps(newProps) {
if(this.props.pageid !== newProps.pageid)
this.props.getFirstEight(newProps.pageid);
}
componentDidMount() {
if(this.props.pageid)
this.props.getFirstEight(this.props.pageid);
}
handleNextClick = (event) => {
event.preventDefault();
this.props.getNextEight(this.props.pageid, this.props.lastNum, this.props.ttlShown);
}
render() {
let graphcards = null;
let disableNext = null;
if (this.props.lastNum >= this.props.ttl)
disableNext = true;
if(this.props.graphs && this.props.graphs.length > 0) {
graphcards = ...
}
return (
<div className={classes.Shell}>
{graphcards}
{this.props.lastNum < this.props.ttl ? (
<div className={classes.NavBtns}>
<RaisedButton disabled={disableNext} onClick={this.handleNextClick}>{'V'}</RaisedButton>
</div>
):null}
</div>
);
}
}
const mapStateToProps = ({pageGraphs}) => {
const { graphs, ttl, lastNum, ttlShown } = pageGraphs;
return {
graphs, ttl, lastNum, ttlShown
};
};
const mapDispatchToProps = {
getFirstEight, getNextEight
};
export default connect(
mapStateToProps, mapDispatchToProps
)(GraphsContainer);
Actions
export const getFirstEight = (pageid) => {
let str = ...;
return (dispatch) => {
axios.get( str )
.then( response => {
let data = {};
let graphs;
let ttl;
let newLastNum = 0;
if((typeof response.data !== 'undefined') && (response.data !== null)) {
data = {...response.data};
ttl = data.total;
if(ttl <= 8) {
graphs = [...data.graphs];
newLastNum = ttl;
} else {
graphs = [...data.graphs].slice(0,8);
newLastNum = 8;
}
}
dispatch({type: GET_FIRSTEIGHT, payload: {ttl,graphs, lastNum:newLastNum}});
} )
.catch( error => {
console.log('ERROR FETCHING NAV TREE', error);
dispatch({type: GET_FIRSTEIGHT, payload: {}});
} );
};
};
export const getNextEight = (pageid, lastNum, ttlShown) => {
let str = ...;
let newLastNum = 0;
return (dispatch) => {
if(ttlShown < lastNum) {
dispatch({type: SHOW_NEXTFOUR, ttlShown: ttlShown+4});
} else {
axios.get( str )
.then( response => {
// console.log('[RESPONSE]', response);
let data = {};
let graphs;
let ttl;
if((typeof response.data !== 'undefined') && (response.data !== null)) {
data = {...response.data};
ttl = data.total;
if(ttl <= (lastNum+8)) {
graphs = [...data.graphs].slice(lastNum);
newLastNum = ttl;
} else {
graphs = [...data.graphs].filter((el,index) => {
return (index > (lastNum-1)) && (index < (lastNum+8));
});
newLastNum = lastNum+8;
}
}
dispatch({type: GET_NEXTEIGHT, payload: {ttl,graphs, lastNum:newLastNum, ttlShown: ttlShown+4}});
} )
.catch( error => {
console.log('ERROR FETCHING NAV TREE', error);
dispatch({type: GET_NEXTEIGHT, payload: {}});
} );
}
};
};

Resources