How to pass props to styled component inside a loop - javascript

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { Container } from './styles';
import { MdContentCopy, MdGroup, MdPerson, MdMovie, MdSettings } from 'react-icons/md';
const items = [
{
route: '/',
icon: <MdContentCopy />,
title: 'Orders',
},
{
route: '/customers',
icon: <MdGroup />,
title: 'Customers',
},
{
route: '/movies',
icon: <MdMovie />,
title: 'Movies',
},
{
route: '/settings',
icon: <MdSettings />,
title: 'Settings',
},
{
route: '/Profile',
icon: <MdPerson />,
title: 'Profile',
},
];
class ItemList extends Component {
state = {
active: false,
};
render() {
const { open, history } = this.props;
const pathName = history.location.pathname;
return (
<Container open={open} active={this.state.active}> // PASSING ACTIVE PROPS TO STYLED COMPONENT
{items.map((item, index) => {
if (item.route === pathName) this.setState({ active: true }); // THIS THROWS AN ERROR BECAUSE TOO MANY RE-RENDERS
return (
<Link to={item.route} key={index}>
{item.icon}
<span>{item.title}</span>
</Link>
);
})}
</Container>
);
}
}
export default ItemList;
I am trying to pass active props to my styled component (Container) inside the loop. I tried it with setState to trigger a re-render because if I just assign a variable (let active = false and if the if statement is true then active = true) it won't re-render the component and active will always be false. But setState inside a loop makes a ton of re-renders and throws a depth exceeded error. Any ideas of how I could do this?

No need to setup the state in this use case (use item.route === pathName instead of this.state.active), just pass the active value as true or false to component, here is revised class mentioned below.
But in this use case matching one route will pass to the container as active= true.
class ItemList extends Component {
render() {
const { open, history } = this.props;
const pathName = history.location.pathname;
const isActive = items.filter(item => item.route === pathName).length > 0;
return (
<Container open={open} active={isActive}> // PASSING ACTIVE PROPS TO STYLED COMPONENT
{items.map((item, index) => {
return (
<Link to={item.route} key={index}>
{item.icon}
<span>{item.title}</span>
</Link>
);
})}
</Container>
);
}
}

Just I have copied your render method changes the concept here. Just I have checked the activeStatus in render method and pass it. For any state change render will be called and that time it will remake the activeStatus.
render() {
const { open, history } = this.props;
const pathName = history.location.pathname;
//code here to check the pathName
let activeStatus = (items.filter(item => item.route == pathName) || []).length > 0 ? true : false;
return (
<Container open= { open } active = { activeStatus } >
{
items.map((item, index) => {
return (
<Link to= { item.route } key = { index } >
{ item.icon }
< span > { item.title } < /span>
< /Link>
);
})
}
</Container>
);
}

Related

Meteor and react map returing undefined, I know the data is there but it loads, despite waiting for isLoading

I have the following code that passing leadsBuilder props to lead in the LeadBuilderSingle componenet. It has an array in a object and I access that array and try to map over it but it returns undefined. The data is being waited on and I am using isLoading, so I am not sure what is causing this error. It loads on first loading, but on page refresh gives me undefined.
import React, { useState, useEffect } from "react";
import Dasboard from "./Dashboard";
import { Container } from "../styles/Main";
import { LeadsBuilderCollection } from "../../api/LeadsCollection";
import { LeadBuilderSingle } from "../leads/LeadBuilderSingle";
import { useTracker } from "meteor/react-meteor-data";
const LeadCategoriesAdd = ({ params }) => {
const { leadsBuilder, isLoading } = useTracker(() => {
const noDataAvailable = { leadsBuilder: [] };
if (!Meteor.user()) {
return noDataAvailable;
}
const handler = Meteor.subscribe("leadsBuilder");
if (!handler.ready()) {
return { ...noDataAvailable, isLoading: true };
}
const leadsBuilder = LeadsBuilderCollection.findOne({ _id: params._id });
return { leadsBuilder };
});
return (
<Container>
<Dasboard />
<main className="">
{isLoading ? (
<div className="loading">loading...</div>
) : (
<>
<LeadBuilderSingle key={params._id} lead={leadsBuilder} />
</>
)}
</main>
</Container>
);
};
export default LeadCategoriesAdd;
import React from "react";
export const LeadBuilderSingle = ({ lead, onDeleteClick }) => {
console.log(lead);
return (
<>
<li>{lead.type}</li>
{lead.inputs.map((input, i) => {
return <p key={i}>{input.inputType}</p>;
})}
</>
);
};
FlowRouter.route("/leadCategories/:_id", {
name: "leadeBuilder",
action(params) {
mount(App, {
content: <LeadCategoriesAdd params={params} />,
});
},
});
try this :
lead.inputs && lead.inputs.map ((input, i) => {...}

How to pass my onSucceeded() function to the parent component?

I have 2 components OptinPage (parent) and TermsOfServices (child). Optin Page is only used for rendering the TermsOfServices component, which can be reused elsewhere in the application. I want to use my onSucceeded () function from my child component to my parent component. I don't see how to do it at all. Currently the result is such that when I click on the button that validates the TermsOfServices it seems to be an infinite loop, it goes on and on without closing my popup. Before I split my TermsOfServices component into a reusable component it worked fine. Before, all content was gathered in OptinPage. Any ideas? Thanks in advance
my TermsOfServices component:
import API from 'api';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import {
Block,
BlockTitle,
Col,
Fab,
Icon,
Preloader,
} from 'framework7-react';
import { FormattedMessage } from 'react-intl';
import { connect } from 'react-refetch';
import ReactHtmlParser from 'react-html-parser';
class TermsOfServices extends PureComponent {
static propTypes = {
agreeTosFunc: PropTypes.func.isRequired,
agreeTos: PropTypes.object,
onSucceeded: PropTypes.func,
tos: PropTypes.object.isRequired,
};
static contextTypes = {
apiURL: PropTypes.string,
loginToken: PropTypes.string,
userId: PropTypes.string,
};
static defaultProps = {
agreeTos: {},
onSucceeded: () => {},
};
state = {
currentTos: -1,
};
componentDidUpdate(prevProps) {
const {
agreeTos,
onSucceeded,
tos,
} = this.props;
const { currentTos } = this.state;
/* Prepare for first tos after receiving all of them */
if (
prevProps.tos.pending &&
tos.fulfilled &&
tos.value.length &&
currentTos < 0
) {
this.setState({ currentTos: 0 });
}
/* When sending ToS agreement is done */
if (
prevProps.agreeTos.pending &&
agreeTos.fulfilled
) {
onSucceeded();
}
}
handleNext = () => {
const { agreeTosFunc, tos } = this.props;
const { currentTos: currentTosId } = this.state;
const termsOfServices = tos.value;
const done = currentTosId + 1 === termsOfServices.length;
this.setState({ currentTos: currentTosId + 1 });
if (done) {
agreeTosFunc(termsOfServices.map((v) => v._id));
}
};
render() {
const { tos } = this.props;
const { currentTos: currentTosId } = this.state;
const termsOfServices = tos.value;
const currentTermsOfServices = termsOfServices && termsOfServices[currentTosId];
const loaded = termsOfServices && !tos.pending && tos.fulfilled;
const htmlTransformCallback = (node) => {
if (node.type === 'tag' && node.name === 'a') {
// eslint-disable-next-line no-param-reassign
node.attribs.class = 'external';
}
return undefined;
};
return (
<div>
{ (!loaded || !currentTermsOfServices) && (
<div id="
optin_page_content" className="text-align-center">
<Block className="row align-items-stretch text-align-center">
<Col><Preloader size={50} /></Col>
</Block>
</div>
)}
{ loaded && currentTermsOfServices && (
<div id="optin_page_content" className="text-align-center">
<h1>
<FormattedMessage id="press_yui_tos_subtitle" values={{ from: currentTosId + 1, to: termsOfServices.length }} />
</h1>
<BlockTitle>
{ReactHtmlParser(
currentTermsOfServices.title,
{ transform: htmlTransformCallback },
)}
</BlockTitle>
<Block strong inset>
<div className="tos_content">
{ReactHtmlParser(
currentTermsOfServices.html,
{ transform: htmlTransformCallback },
)}
</div>
</Block>
<Fab position="right-bottom" slot="fixed" color="pink" onClick={() => this.handleNext()}>
{currentTosId + 1 === termsOfServices.length &&
<Icon ios="f7:check" aurora="f7:check" md="material:check" />}
{currentTosId !== termsOfServices.length &&
<Icon ios="f7:chevron_right" aurora="f7:chevron_right" md="material:chevron_right" />}
</Fab>
{currentTosId > 0 && (
<Fab position="left-bottom" slot="fixed" color="pink" onClick={() => this.setState({ currentTos: currentTosId - 1 })}>
<Icon ios="f7:chevron_left" aurora="f7:chevron_left" md="material:chevron_left" />
</Fab>
)}
</div>
)}
</div>
);
}
}
export default connect.defaults(new API())((props, context) => {
const { apiURL, userId } = context;
return {
tos: {
url: new URL(`${apiURL}/tos?outdated=false&required=true`),
},
agreeTosFunc: (tos) => ({
agreeTos: {
body: JSON.stringify({ optIn: tos }),
context,
force: true,
method: 'PUT',
url: new URL(`${apiURL}/users/${userId}/optin`),
},
}),
};
})(TermsOfServices);
My OptIn Page :
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import {
Link,
NavRight,
Navbar,
Page,
Popup,
} from 'framework7-react';
import { FormattedMessage, intlShape } from 'react-intl';
import './OptInPage.scss';
import TermsOfServices from '../components/TermsOfServices';
class OptinPage extends PureComponent {
static propTypes = {
logout: PropTypes.func.isRequired,
opened: PropTypes.bool.isRequired,
};
static contextTypes = {
intl: intlShape,
logout: PropTypes.func,
};
render() {
const { opened, logout } = this.props;
const { intl } = this.context;
const { formatMessage } = intl;
return (
<Popup opened={opened} className="demo-popup-swipe" tabletFullscreen>
<Page id="optin_page">
<Navbar title={formatMessage({ id: 'press_yui_tos_title' })}>
<NavRight>
<Link onClick={() => logout()}>
<FormattedMessage id="press_yui_comments_popup_edit_close" />
</Link>
</NavRight>
</Navbar>
</Page>
<TermsOfServices onSucceeded={this.onSuceeded} />
</Popup>
);
}
}
export default OptinPage;
Just add the data you want the parent to be supplied with in the child component (when it is hit) and then handle the data passed to the parent in the function that you pass in onSuccess.
This will roughly look like this:
const {useState, useEffect} = React;
function App(){
return <Child onSuccess={(data)=>{console.log(data)}}/>;
}
function Child({onSuccess}){
return <div>
<button
onClick={()=>onSuccess("this is the data from the child component")}>
Click to pass data to parent
</button>
</div>;
}
ReactDOM.render(<App/>,document.getElementById('app'));
#element {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='app'></div>
<div id="element">
<div>node 1</div>
<div>node 2</div>
</div>
to access to parent method or attribute you should use super,
for call to the parent constructor
super([arguments]);
for call parent method
super.parentMethod(arguments);
I recommend create a method on child class and then call the parent method, not directly
for more information take a look on this
https://www.w3schools.com/jsref/jsref_class_super.asp

How to Update the state in react redux-saga

I am newbie to react, redux-saga, I have a dropdown in page Display, when I select it move to respective component (eg. policy, score), In Component Pages, I have a button Add New, on clicking it will navigate to a page as mentioned in link url , which is a page having cancel button, on cancelling it returns to the Display.js but no dropdown selected,
I would like to keep the state articleMode, when navigating to a page and returning back to same page,
articleMode returns to state -1 instead of selected component Policy or Score
actions.js
export const updateArticleMode = data => {
console.log(data.body);
return {
type: CONSTANTS.UPDATE_ARTICLE_MODE,
data: data.body
};
};
queryReducer.js
import * as CONSTANTS from "../constants/constants";
const initialState = {
articleMode: ""
}
case CONSTANTS.UPDATE_ARTICLE_MODE: {
return {
...state,
articleMode: data.mode
};
}
export default queryReducer;
constants.js
export const UPDATE_ARTICLE_MODE = "UPDATE_ARTICLE_MODE";
Display.js
import React from "react";
import { connect } from "react-redux";
import Policy from "../policy";
import Score from "./../score";
import { updateArticleMode } from "../../../../actions/actions";
const articleMode = [
{ name: "Select", id: "-1" },
{ name: "Score", id: "Score" },
{ name: "Policy", id: "Policy" }
]
class Display extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
articleMode: "-1"
};
}
componentWillMount = () => {
this.setState({ articleMode: this.props.queryData.articleMode || "-1" });
};
_getComponent = () => {
const { articleMode } = this.state;
if (articleMode === "Policy") {
return <DisplayPolicy></DisplayPolicy>;
}
if (articleMode === "Score") {
return <DisplayScore></DisplayScore>;
}
}
render() {
return (
<React.Fragment>
<select name="example"
className="simpleSearchSelect1"
value={this.state.articleMode}
onChange={event => {
this.setState({ articleMode: event.target.value });
this.props.dispatch(
updateArticleMode({ body: { mode: event.target.value } })
);
}}
style={{ marginLeft: "2px" }}
>
{articleMode.length != 0 &&
articleMode.map((option, index) => {
const { name, id } = option;
return (
<option key={index} value={id}>
{name}
</option>
);
})}
</select>
{this.state.articleMode === "-1"
? this._renderNoData()
: this._getComponent()}
</React.Fragment>
)}
const mapStateToProps = state => {
return {
queryData: state.queryData
};
};
export default connect(mapStateToProps)(Display);
}
DisplayPolicy.js
import React from "react";
class DisplayPrivacyPolicy extends React.Component {
constructor(props) {
super(props);
}<Link
to={{
pathname: "/ui/privacy-policy/addNew",
state: {
language: "en"
}
}}
>
<button className="page-header-btn icon_btn display-inline">
<img
title="edit"
className="tableImage"
src={`${process.env.PUBLIC_URL}/assets/icons/ic_addstore.svg`}
/>
{`Add New`}
</button>
</Link>
AddNew.js
<Link
to =pathname: "/ui/display",
className="btn btn-themes btn-rounded btn-sec link-sec-btn"
>
Cancel
</Link>

ReactJS | Loading State in component doesn't render Spinner

I am trying to make a React component that displays multiple renders based on props and state. So, while I wait for the promise to be resolved, I want to display a spinner Component
Main Renders:
NoResource Component => When the user is not valid
Spinner Component => When is loading on all renders
BasicRender Component => When data are fetched and is not loading
Below is my component:
/* eslint-disable react/prefer-stateless-function */
import React, { Component, Fragment } from 'react';
import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';
import { getUser, listUsers } from '../../config/service';
export class UserDetailsScreen extends Component {
static propTypes = {
match: PropTypes.shape({
isExact: PropTypes.bool,
params: PropTypes.object,
path: PropTypes.string,
url: PropTypes.string
}),
// eslint-disable-next-line react/forbid-prop-types
history: PropTypes.object,
label: PropTypes.string,
actualValue: PropTypes.string,
callBack: PropTypes.func
};
state = {
user: {},
error: '',
isloading: false
};
componentDidMount() {
this.fetchUser();
this.setState({ isLoading: true})
}
getUserUsername = () => {
const { match } = this.props;
const { params } = match;
return params.username;
};
fetchUser = () => {
getUser(this.getUserUsername())
.then(username => {
this.setState({
user: username.data,
isloading: false
});
})
.catch(({ message = 'Could not retrieve data from server.' }) => {
this.setState({
user: null,
error: message,
isLoading: false
});
});
};
validateUsername = () =>
listUsers().then(({ data }) => {
const { match } = this.props;
if (data.includes(match.params.username)) {
return true;
}
return false;
});
// eslint-disable-next-line no-restricted-globals
redirectToUsers = async () => {
const { history } = this.props;
await history.push('/management/users');
};
renderUserDetails() {
const { user, error } = this.state;
const { callBack, actualValue, label, match } = this.props;
return (
<div className="lenses-container-fluid container-fluid">
<div className="row">
.. More Content ..
{user && <HeaderMenuButton data-test="header-menu-button" />}
</div>
{user && this.validateUsername() ? (
<Fragment>
.. Content ..
</Fragment>
) : (
<div className="container-fluid">
{this.renderNoResourceComponent()}
</div>
)}
<ToolTip id="loggedIn" place="right">
{user.loggedIn ? <span>Online</span> : <span>Oflline</span>}
</ToolTip>
</div>
);
}
renderNoResourceComponent = () => {
const { match } = this.props;
return (
<div className="center-block">
<NoResource
icon="exclamation-triangle"
title="Ooops.."
primaryBtn="« Back to Users"
primaryCallback={this.redirectToUsers}
>
<h5>404: USER NOT FOUND</h5>
<p>
Sorry, but the User with username:
<strong>{match.params.username}</strong> does not exists
</p>
</NoResource>
</div>
);
};
renderSpinner = () => {
const { isLoading, error } = this.state;
if (isLoading && error === null) {
return <ContentSpinner />;
}
return null;
};
render() {
return (
<div className="container-fluid mt-2">
{this.renderSpinner()}
{this.renderUserDetails()}
</div>
);
}
}
export default withRouter(UserDetailsScreen);
The problem is:
I get the spinner along with the main component, and I am getting this error:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.. Can you please tell me what I am doing wrong.
The error is because you are running the renderUserDetailsComponent even when your API call is in loading state. You must only render the spinner on loading state
renderUserDetails() {
const { user, error, isLoading } = this.state;
if(isLoading) {
return null;
}
const { callBack, actualValue, label, match } = this.props;
return (
<div className="lenses-container-fluid container-fluid">
<div className="row">
.. More Content ..
{user && <HeaderMenuButton data-test="header-menu-button" />}
</div>
{user && this.validateUsername() ? (
<Fragment>
.. Content ..
</Fragment>
) : (
<div className="container-fluid">
{this.renderNoResourceComponent()}
</div>
)}
<ToolTip id="loggedIn" place="right">
{user.loggedIn ? <span>Online</span> : <span>Oflline</span>}
</ToolTip>
</div>
);
}

How to use destructing in JavaScript — in a helper function which will be passed to a React component?

I have this component which takes an id as an attribute:
<TeamLogo id={team.id} className="center" />
As you can see its a property attached to an object.
So what I came up with is:
/* helper function */
function TeamIdChecker({ id }) {
if (id === undefined) return <Redirect to="/" />;
else return team.id;
}
And then i'd like to use it like this:
<TeamLogo id={TeamIdChecker(team.id)} className="center" />
I didn't try it as I know I'm off!
Thanks my friends in advance!
Update
This is my Team component:
import { Component } from "react";
import PropTypes from "prop-types";
import { getTeam } from "../api";
export default class Team extends Component {
static propTypes = {
id : PropTypes.string.isRequired,
children: PropTypes.func.isRequired
};
state = {
team: null
};
componentDidMount() {
this.fetchTeam(this.props.id);
}
componentWillReceiveProps(nextProps) {
if (this.props.id !== nextProps.id) {
this.fetchTeam(nextProps.id);
}
}
fetchTeam = id => {
this.setState(() => ({ team: null }));
getTeam(id).then(team => this.setState(() => ({ team })));
};
render() {
return this.props.children(this.state.team);
}
}
This is my TeamLogo component:
import React from "react";
import PropTypes from "prop-types";
const logos = {
// logo key and values
};
TeamLogo.propTypes = {
id: PropTypes.string.isRequired
};
TeamLogo.defaultProps = {
width: "200px"
};
export default function TeamLogo(props) {
return (
<svg {...props} x="0px" y="0px" viewBox="0 0 125.397 125.397">
{logos[props.id]}
</svg>
);
}
You don't want that <Redirect to="/" /> to be passed as a property to TeamLogo, right? I'd just use
if (team.id === undefined)
return <Redirect to="/" />;
else
return <TeamLogo id={team.id} className="center" />
You could do some conditional rendering
function TeamIdChecker({ id }) {
if (id === undefined) return false;
else return true;
}
then
render() { // where your rendering your component
const { id } = team; // wherever that come from, you destruct it here
return(
<React.Fragment>
{TeamIdChecker(id) ? <TeamLogo id={id} className="center" /> : <Redirect to="/" />}
</React.Fragment>
)
}
edit:
or even simpler if this helper function its only used here
render() { // where your rendering your component
const { id } = team; // wherever that come from, you destruct it here
return(
<React.Fragment>
{id !== undefined ? <TeamLogo id={id} className="center" /> : <Redirect to="/" />}
</React.Fragment>
)
}

Categories