Tabs Component including Cards crashing - reactjs

I have to build a web application using React & Material-UI frameworks.
I use a tab component to display mutliple cards (includiing charts) inside. The first tab works pretty good, but when I want to switch to the second tab, there is no displaying inside the tab. Worst, I can't return to the first tab ; the Tabs Component is completely dead.
You can find my code here :
class NavTabs extends React.Component {
state = {
value: 0,
};
handleChange = (value) => {
this.setState({ value });
};
render() {
const { classes } = this.props;
const { value } = this.state;
return (
<div className={classes.root}>
<AppBar position="static">
<Tabs fullWidth value={value} onChange={this.handleChange}>
<Tab label="FINANCIAL" />
<Tab label="OPERATIONAL" />
<Tab label="PLANNING" />
</Tabs>
</AppBar>
{value === 0 && <TabContainer>
<div className={classes.card_up}>
<CashWindow />
<SalesWindow />
</div>
<div className={classes.card_up}>
<CostsWindow />
<MarginsWindow />
<ContingenciesWindow />
</div>
<Button className={classes.button_more}>Details</Button>
</TabContainer>}
{value === 1 && <TabContainer>
<div className={classes.card_up}>
<OpportunitiesWindow />
<CriticalIssuesWindow />
</div>
<div className={classes.card_up}>
<QualityWindow />
</div>
<Button className={classes.button_more}>Details</Button>
</TabContainer>}
{value === 2 && <TabContainer>
<div className={classes.card_up}>
<ResourcesWindow />
</div>
</TabContainer>}
</div>
);
}
}
I don't have any error with the others components, because when I am trying to move components of the second tab into the first, it works. My conclusion is that the problem come from the Tabs Component.
Does anyone know how to fix this ?
Maybe I am doing wrong, I am a beginner in React programming.

Related

React Router v6 Button onclick Navigate vis class components

Trying to port an application from router v2 to router v6.
Everything I see everywhere is hooks.
Tried to make a custom withRouter, "Hooks can only be called inside of the body of a function component."
The button in question
<YAButton
onClick={this.goToForgotPassword.bind(this)}
size="tiny"
style={YALogin.styles.forgotPassword}
type="text"
>
the function it calls
goToForgotPassword() {
this.props.navigate(`/forgot${isEmail(this.state.emailAddress) ?
`?emailAddress=${encodeURIComponent(this.state.emailAddress)}` :
''
}`);
}
the export at the bottom of the jsx file, perhaps something is amiss here?
export default connect(YALogin.mapStateToProps, YALogin.mapDispatchToProps)(Radium(withRouter(YALogin)));
and my withRouter
export const withRouter = (Component) => {
const Wrapper = (props) => {
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
return (
<Component
{...props}
navigate={navigate}
location={location}
params={params}
/>
);
};
return Wrapper;
};
Clearly I can't use hooks, but I also can't change the entire application over to function based components.
How do click "Forgot Password" button to go the
`/forgot${isEmail(this.state.emailAddress) ?
`?emailAddress=${encodeURIComponent(this.state.emailAddress)}` :
''
}`
edit: The render().
render() {
return (
<main
className="splash"
style={{
backgroundImage: `url(${window.__configuration.cdn}/splash/splash.jpg)`
}}
>
<form
className="stripped"
onSubmit={this.sendLoginRequest.bind(this)}
>
{/* Render the application logo */}
<div className="dual-logo">
<img
src={`${window.__configuration.cdn}/logos/logo-redacted-y-negative.svg`}
className="redacted-logo"
alt="Weemarkable"
/>
<div className="logo-divider"></div>
<img
src={`${window.__configuration.cdn}/logos/logo-weemarkable-negative-${Translator.getLang()}.svg`}
className="weemarkable-logo"
alt="Weemarkable"
/>
</div>
{/* Set inner html to allow non-breaking-space tags to render */}
<p className="tagline"
dangerouslySetInnerHTML={{__html: Translator.translate("General.Tagline")}}></p>
<fieldset>
{/* Render an alert if the login has failed for any reason */}
{
this.props.loginError !== null ?
<YAAlert type="E">
<span>{this.state.errorMessage}</span>
</YAAlert> :
null
}
{/* Collect the email address from the user */}
<YAInput
type="email"
className="stripped-input"
noBaseStyles
onChange={this.onChange.bind(this, 'emailAddress')}
placeholder={Translator.translate("General.YourEmail")}
value={this.state.emailAddress}
/>
{/* Collect the password from the user */}
<YAInput
type="password"
className="stripped-input"
noBaseStyles
onChange={this.onChange.bind(this, 'password')}
placeholder={Translator.translate("Password.Password")}
value={this.state.password}
/>
<div style={YALogin.styles.controls}>
{/* Button which submits the login request to the server */}
<div>
<YAInput
type="submit"
className={classNames('btn-stripped', {disabled: !this.isFormValid()})}
noBaseStyles
value={Translator.translate("MainPage.SignIn")}
/>
</div>
{/* Button which takes the user to the forgot password screen */}
<div>
<YAButton
onClick={() => useNavigate()(`/forgot${isEmail(this.state.emailAddress) ? `?emailAddress=${encodeURIComponent(this.state.emailAddress)}` : ''}`)}
size="tiny"
style={YALogin.styles.forgotPassword}
type="text"
>
{Translator.translate("MainPage.ForgotPassword")}
</YAButton>
|
<YAButton
onClick={this.goToForgotPassword.bind(this)}
size="tiny"
style={YALogin.styles.forgotPassword}
type="text"
>
{Translator.translate("MainPage.TOU")}
</YAButton>
</div>
</div>
</fieldset>
</form>
{/* Render footer logo */}
<footer className="brand-bar login">
<img
src={`${window.__configuration.cdn}/logos/logo-redacted-of-hamilton-${window.__configuration.langCode}.svg`}
className="footer-logo"
alt="redactedof redacted| redacted| Brantford"
/>
</footer>
</main>
);
}
}

How to build Tabs component Reactjs

I'm trying to build a Tabs component with ReactJS and I'm following a CSS stylesheet.So, I would love to have this as render :
<div data-container="" class="tabs tabs-horizontal">
<div class="tabs-header">
<div class="tabs-header-tab ph active" data-tab="0" style="transform: translateX(0px) translateZ(0px);">Personal
</div>
<div class="tabs-header-tab ph undefined" data-tab="0" style="transform: translateX(0px) translateZ(0px);">Job
</div>
<div class="tabs-header-tab ph undefined" data-tab="0" style="transform: translateX(0px) translateZ(0px);">
Salary</div>
</div>
<div data-container="" class="tabs-content">
<div data-container="" class="tabs-content-tab open" data-tab="0">
<div>I am a HR Specialist.</div>
</div>
<div data-container="" class="tabs-content-tab undefined" data-tab="0">
<div>No records have been added yet</div>
</div>
<div data-container="" class="tabs-content-tab undefined" data-tab="0">
<div>This type of information needs to be added by your manager</div>
</div>
</div>
</div>
And this is what the components looks like now :
<Tabs>
<TabHeaders>
<TabHeadersItem text="Personal" state="active" />
<TabHeadersItem text="Job" />
<TabHeadersItem text="Salary" />
</TabHeaders>
<TabContents>
<TabContentsItem
content="I am a HR Specialist."
open="open"
/>
<TabContentsItem content="No records have been added yet" />
<TabContentsItem content="This type of information needs to be added by your manager" />
</TabContents>
</Tabs>
So, I'm trying to find a solution, when user clicks on a header it becomes "active" and its content "open" while the others should be closed and not active.
Ok so since you've edited your question, I'll do with mine. The idea is the same: To keep the state of which tab is open, and since you don't want to keep your tabs in a list, then you need to link a TabHeader to a TabContent:
const TabHeaderItem = props => {
return (
<div
onClick={() => props.changeTab(props.text.toLowerCase())}
className={props.state ? "active" : ""}
>
{props.text}
</div>
);
};
const TabContentsItem = props => {
return <div className={props.open ? "open" : "closed"}>{props.content}</div>;
};
const TabComponent = () {
const [active, setActive] = useState("personal");
const changeTab = tab => {
console.log("tab", tab);
setActive(tab);
};
return (
<div className="App">
<div className="tabs">
<TabHeaderItem
changeTab={changeTab}
text="Personal"
state={active === "personal"}
/>
<TabHeaderItem
changeTab={changeTab}
text="Job"
state={active === "job"}
/>
<TabHeaderItem
changeTab={changeTab}
text="Salary"
state={active === "salary"}
/>
</div>
<div className="tabs-content">
<TabContentsItem
content="I am a HR Specialist."
open={active === "personal"}
/>
<TabContentsItem
content="No records have been added yet"
open={active === "job"}
/>
<TabContentsItem
content="This type of information needs to be added by your manager"
open={active === "salary"}
/>
</div>
</div>
);
}
You can check out the working example here: https://codesandbox.io/s/exciting-gauss-lbghq

Reusing multiple instances of react component with different props

So I have a child component that I want to render multiple instances of in a parent container component. Passing in different props to each so they display differently.
What is happening is that they are both being rendered with the last instance of the props in the script being read into both instances. Thus the both components below end up with placeHolder==='Describe yourself'
Is there a work around for this so that they will each be injected with their props in turn exclusively?
<ButtonMode
open={this.state.open}
handleClose={this.handleClose}
buttonName='Update'
modalOpen={this.modalOpen}
placeHolder="New picture url"
change={this.handlePicture}
label='URL'
/>
<ButtonMode
open={this.state.open}
handleClose={this.handleClose}
buttonName='Update'
modalOpen={this.modalOpen}
placeHolder='Describe yourself'
label='Bio'
change={this.handleBio}
/>
ButtonMode
class ButtonMode extends Component {
constructor(props){
super(props)
this.state = {
input:''
}
this.handleInput = this.handleInput.bind(this);
this.handle = this.handle.bind(this);
}
handleInput(val){
this.setState({input:val})
};
handle() {
this.props.change(this.state.input);
};
render(){
const { classes } = this.props;
return (
<div>
<Button
className={classes.button}
onClick={this.props.modalOpen}
>Update
</Button>
<Modal
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
open={this.props.open}
onClose={this.props.handleClose}
>
<div className={classes.paper}>
<TextField
id="filled-textarea"
label={this.props.label}
placeholder={this.props.placeHolder}
multiline
className={classes.textField}
onChange={(e)=>{this.handleInput(e.target.value)}}
rows= '4'
/>
<Button
onClick={this.handle}
className={classes.button}
color="secondary">Submit</Button>
</div>
</Modal>
</div>
)
}
}
Then I used it like that
class UserCard extends Component {
constructor(props){
super(props);
this.state = {
tempPro:'',
open: false,
profilePicture:''
}
this.modalOpen = this.modalOpen.bind(this);
this.handleClose = this.handleClose.bind(this);
this.handlePicture = this.handlePicture.bind(this);
}
// componentDidMount(){
// const {userId, profilePic} = this.props;
// this.setState({profilePicture:profilePic});
// // axios.get(`/api/profile/${userId}`).then(res=>{
// // let {profilePic} = res.data[0];
// // this.setState({profilePic})
// // })
// }
handlePicture(val){
this.props.changePic(val);
this.setState({open:false});
};
handleBio(val){
this.setState({open:false});
};
handleClose(){
this.setState({open: false});
};
modalOpen(){
this.setState({open:true});
};
render() {
const { classes } = this.props;
const {stories} = this.props;
let storyShow = stories.map((story,id) => {
return(
<div value={story.story_id}>
<h3>{story.title}</h3>
<ul className={classes.background}>
<li>{story.description}</li>
<li>{story.is_complete}</li>
</ul>
</div>
)
});
return (
<div className={classes.rootD}>
<Grid container>
<Grid className={classes.itemFix} >
<Card className={classes.card}>
<CardMedia
className={classes.media}
image={this.props.proPic}
title={this.props.userName}
/>
<div>
<ButtonMode
open={this.state.open}
handleClose={this.handleClose}
modalOpen={this.modalOpen}
placeHolder="New picture url"
change={this.handlePicture}
label='URL'
/>
</div>
<CardHeader
className={classes.titles}
title={this.props.userName}
subheader="Somewhere"
/>
<CardHeader className={classes.titles} title='Bio' />
<CardContent className={classes.background}>
<Typography className={classes.bio} paragraph>
{this.props.bio}
</Typography>
</CardContent>
<div>
<ButtonMode
open={this.state.open}
handleClose={this.handleClose}
modalOpen={this.modalOpen}
placeHolder='Describe you how you want'
label='Bio'
change={this.handleBio}
/>
</div>
</Card>
</Grid>
<Grid className={classes.itemFixT}>
<Card className={classes.card}>
<CardContent>
<CardHeader
className={classes.titles}
title='Works'/>
<Typography paragraph>
<ul>
{storyShow}
</ul>
</Typography>
</CardContent>
</Card>
</Grid>
</Grid>
</div>
);
}
}
UserCard.propTypes = {
classes: PropTypes.object.isRequired,
};
function mapStateToProps(state){
const {userId, profilePic} = state;
return {
userId,
profilePic
}
}
export default connect(mapStateToProps,{})(withStyles(styles)(UserCard));
I had a similar issue where I was trying to pass different functions to the children components. I had a UploadFile component that contained an <input/> and a <Button/> from material-ui, and I wanted to reuse this component multiple times throughout a page, as the user has multiple files to upload, and in order to save the files, I needed callback functions in the main page.
What I had to do, was give each child component <UploadFile/> in my case, and <ButtonMode/> in your case, a unique id passed in as a prop, since otherwise, the top level page cannot tell each reference to the child component apart from any others.
The code of the child component:
function UploadFile({ value, handleFile }) {
const classes = useStyles();
return (
<>
<input
accept=".tsv,.fa,.fasta"
className={classes.input}
id={value}
type="file"
style={{ display: 'none' }}
onChange={e => handleFile(e.target.files[0])}
/>
<label htmlFor={value}>
<Button
variant="contained"
color='default'
component="span"
startIcon={<CloudUploadIcon />}
className={classes.button}>
Upload
</Button>
</label>
</>
);
}
The usage of this component in the parent (handleFile is the function I am passing in and is defined above in the parent component):
<UploadFile value='prosite' handleFile={handlePrositeFile} />
<UploadFile value='pfam' handleFile={handlePfamFile} />
I spent an embarrassingly long time on a similar issue. I tried all sorts of JS debugging and even re-read the entire concept of closure :)
This is was my culprit: <TextField id="filled-textarea" ... />
i.e. the id is static. If we have multiple instances of the same id on one page, we have a problem.
Make id dynamic, e.g. <TextField id={this.props.label} ... />
I was using the same state for both modals and in each instance of handleOpen() it was only ever opening the last instance of modal in the script.

Material-UI custom JSS Styles removed on Component Re-render

I have a temporary Drawer with Tabs inside. When the Drawer is open and the color theme is toggled, the custom styles for the component in the tab are removed, breaking the styling. If I close the Drawer or toggle between tabs, it fixes the issue. This is only happening in two of the tabs, not all of them. See the pictures below.
For simplicity, I'll show the code for the Check Results tab. The Cog icon is being stripped of its styling and defaulting to the left.
...
const styles: StyleRulesCallback = () => ({
buttonDiv: {
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'flex-end',
},
});
...
type Props = StateProps & DispatchProps & WithStyles<typeof styles>;
class CheckResultsPanel extends React.Component<Props> {
...
render() {
this.selectedKeys = [];
const tree = this.renderTree();
const { classes } = this.props;
return (
<div id="checkResultsPanel">
<div className={classes.buttonDiv}>
<IconButton onClick={this.designSettingsClick} aria-label="Design Settings">
<SettingsIcon />
</IconButton>
</div>
<Divider />
{tree}
</div>
);
}
The components in question are being imported to the Drawer like so:
<div className="right-sidebar-container">
<div id="loadCaseObjectTabs" className={classes.root}>
<AppBar position="static">
<Tabs value={this.state.topValue} onChange={this.topHandleChange} fullWidth>
<Tab label="Objects" />
<Tab label="Load Cases" />
</Tabs>
</AppBar>
{topValue === 0 ? <div className={classes.tabs}><NavigationPanel /></div> : undefined}
{topValue === 1 ? <div className={classes.tabs}><LoadCasesPanel /></div> : undefined}
</div>
<div id="checkResultsPropertiesTabs" className={classes.root}>
<AppBar position="static">
<Tabs value={bottomTabIndices[this.props.bottom]} onChange={this.bottomHandleChange} fullWidth>
<Tab label="Check Results" />
<Tab label="Properties" />
</Tabs>
</AppBar>
{this.props.bottom === 'checkResults' ? <div className={classes.tabs}><CheckResultsPanel /></div> : undefined}
{this.props.bottom === 'properties' ? <div className={classes.tabs}><PropertiesPanel /></div> : undefined}
</div>
</div>
We discovered the issue. Apologies for the vague example. The issue was caused by optimization we implemented. The component was only set to update if certain props were changed and the classes were not being passed through this logic. When the themes were toggled, it changed all the styling for the application but since the component did not update the styles were not being applied.
shouldComponentUpdate(nextProps: Props) {
...
if (this.props.classes !== nextProps.classes) return true;
...
}

React/Mobx: Adding checkbox handler breaks a textarea field

I recently added a few checkboxes to a component. In the map function below, 5 of them are rendered.
After attaching a checkbox onchange handler, not only did the check/uncheck action fail to implement, but editing the text of a textarea in this component now breaks the editing function and causes an infinite loop in MobX (in the console).
However, removing the onChange handler from the checkboxes immediately fixes the issue with the textarea.
I don't see how the 2 are related, does anyone know what is happening? Here is the component:
const FinalEditsAndCopy = (props) => {
const update_final_textarea = (text_input) => {
ui_store.set_final_text_message(text_input.target.value);
console.log(text_input.target.value);
};
const render_social_media_checkboxes = () => {
return static_local_data.social_media_sites.map((social_media_site, i) => (
<List.Item key={i}>
<List.Content>
<input
type="checkbox"
checked={ui_store.final_social_media_site_selections[social_media_site]}
name={social_media_site}
className={ checkbox_style }
onChange={ ui_store.reverse_checkbox_state(social_media_site) } // This is the line that breaks the textarea and also does not fill its intended purpose
/>
{Lodash.capitalize(social_media_site)}
</List.Content>
</List.Item>
))
};
const render_social_media_checkboxes_test = () => {
return static_local_data.social_media_sites.map((social_media_site, i) => (
<List.Item key={ i }>
<List.Content>
<p>Test</p>
</List.Content>
</List.Item>
));
};
return (
<div className={ outer_container_style }>
<Container>
<Form>
<TextArea autoHeight
value={ ui_store.final_text_message }
className={ textarea_style }
onChange={ update_final_textarea }
/>
<Message attached='bottom' positive className={ alert_style }>
<Icon
name='clipboard list'
color='green'
size='big'
/>
Copied to clipboard
</Message>
</Form>
<Header size='small'>Select Social Media Sites</Header>
<div>
<List horizontal>
{render_social_media_checkboxes()}
</List>
</div>
<Button size='huge' color='orange'>Copy Text and Open Social Media Sites in New Tabs</Button>
</Container>
</div>
);
};
export default observer(FinalEditsAndCopy);
MobX observables in ui_store:
final_text_message = '';
final_social_media_site_selections = {
"facebook" : true,
"twitter" : true,
"linkedin" : true,
"instagram" : true,
"pinterest" : true
};
And the relevant actions:
set_final_text_message(input_message) {
this.final_text_message = input_message
}
reverse_checkbox_state(social_media_site) {
this.final_social_media_site_selections[social_media_site] = !this.final_social_media_site_selections[social_media_site]
}
You are invoking reverse_checkbox_state directly on render. You want to give onChange a function that will be called when the change event occurs, not invoke the function yourself.
<input
type="checkbox"
checked={ui_store.final_social_media_site_selections[social_media_site]}
name={social_media_site}
className={ checkbox_style }
onChange={() => ui_store.reverse_checkbox_state(social_media_site)}
/>

Resources