I am trying to use Material UI components in NextJS but they are not working properly.
import React, { Component } from "react";
// MATERIAL
import Snackbar from "#material-ui/core/Snackbar";
import MuiAlert from "#material-ui/lab/Alert";
export default class NewProduct extends Component {
constructor() {
super();
this.state = {
testSnack: false,
};
}
testSnackbars = () => {
this.setState({ testSnack: true });
};
render(){
return{
<>
<Snackbar
open={this.state.testSnack}
autoHideDuration={5000}
onClose={this.handleClose}
>
<MuiAlert
elevation={6}
variant="filled"
onClose={this.handleClose}
severity="success"
>
Product Added!
</MuiAlert>
</Snackbar>
<button onClick={this.testSnackbars}>
Save
</button>
</>
}
}
}
This is a simple logic for showing snackbar on button click but this doesnt seem to work in NextJs, I use Material in ReactJs very often and this never happened there. Can someone tell me what I'm doing wrong here?
I am not able to run snackbars using my state. If I pass in open={true} property, it shows up but not working through state variable.
Replace this line
return {...};
with
return (...);
Besides that, everything seems to look good on my codesandbox.
Related
The problem is that the button that is supposed to give the option to print is not working anymore.
the error in the console says:
To print a functional component ensure it is wrapped with `React.forwardRef`, and ensure the forwarded ref is used. See the README for an example: https://github.com/gregnb/react-to-print#examples
I Have already seen some solutions specifically talking about the same problem but I have not been able to make it work.
any suggestion?
this is the library i'm using: ReactToPrint npm
React To print
import { useRef } from "react";
import { useReactToPrint } from "react-to-print";
import Resume from "./Pdf/Pdf";
const Example = () => {
const componentRef = useRef();
const handlePrint = useReactToPrint({
content: () => componentRef.current
});
return (
<div >
<button onClick={handlePrint}> ------> NOT WORKING!
Descargar Pdf
</button>
<Resume ref={componentRef} /> ------> COMPONENT TO PRINT
</div>
);
};
export default Example;
Component to be printed
import React from "react";
import styled from 'styled-components';
import PdfSection from './PdfSection';
import AlienLevel from './AlienLevel';
import {connect } from 'react-redux';
class Resume extends React.Component {
renderList = () => {
return this.props.posts.diagnose.map((post) => {
return (
<PdfSection
key={post.id}
id={post.id}
divider={"/images/pdf/divider.png"}
img={"/images/alienRandom.png"}
title={post.title}
// data={post.data}
text={post.text0}
subtext={post.subtext0}
/>
);
});
};
render(){
return (
<div>
<Container>
<Page>
<Portada>
<img id="portada" src="/images/pdf/PortadaPdf.png" />
</Portada>
</Page>
<Page>
<AlienLevel
result= "{props.diagn}{"
character={"/images/pdf/alienMedio.png"}
fileName={"responseBody[4].data"}
level={"/images/pdf/level6.png"}
correct={"/images/pdf/correct.png"}
medium={"/images/pdf/medium.png"}
incorrect={"/images/pdf/incorrect.png"}
text='"Necesitas mejorar tus prácticas intergalácticas de CV, pero ya eres nivel medio!"'
/>
<div>{this.renderList()}</div>
</Page>
</Container>
</div>
);
};
}
const mapStateToProps = (state) => {
return { posts: state.posts };
};
export default connect(mapStateToProps)( Resume);
thanks in advance!
The problem is with connect() function of react-redux.
You wrapped your component in connect and connect by default does not forward ref. Which means, the ref you are passing here <Resume ref={componentRef} /> does not reach to your component.
You need to pass options { forwardRef: true } in fourth parameter of connect function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?).
Just change this code export default connect(mapStateToProps)(Resume); in Resume component to this
export default connect(mapStateToProps, null, null, { forwardRef: true })(Resume);
For anyone that is struggling with the same error, it seems that they found the proper way to resolve this, I actually resolved it by following the Codesandbox I found in the Github issues here si the link. hope is useful! -->
LINK TO GITHUB SPECIFIC ISSUE (SOLVED!!)
I had the same issue and I am happy to share my findings as soon as now.
The component has to be rendered somewhere using ref.
I added it to my page as hidden using React Material UI's Backdrop. Or u can hide it using hooks like examples below.
Using backdrop and only calling it when I need to preview the print. 👇👇
<Backdrop sx={{ color: "#fff", zIndex: (theme) => theme.zIndex.drawer + 1 }}
open={openBD}>
<ComponentToPrint ref={componentRef} />
</Backdrop>
Using Hooks plus display styling to only display it when needed. 👇👇
const [isReady, setIsReady] = useState("none");
<Paper style={{ display: isReady }} >
<ComponentToPrint ref={componentRef} />
</Paper>
<Button
variant="contained"
endIcon={<BackupTableRoundedIcon />}
onClick={() => setIsReady("")}
>
Start Printing
</Button>
Note: I used MUI components, if u decide to copy paste, then change Button to html <button and paper to <div. Hope this helps.
Since upgrade material-ui to v4, I got some warnings with Modal component.
E.g
Failed prop type: Invalid prop children supplied to ForwardRef(Modal).
Function components cannot be given refs.
This is Modal code (warning 1):
import React from 'react';
import Proptypes from 'prop-types';
...
class DetailDialog extends React.Component {
render() {
const { classes, open, onClose } = this.props;
return (
<Dialog
open={open}
>
...
</Dialog>
);
}
}
DetailDialog.propTypes = {
open: Proptypes.bool,
onClose: Proptypes.func,
};
export default DetailDialog;
This is Modal code (warning 2):
import React from 'react';
import PropTypes from 'prop-types';
...
class SelectCatDialog extends React.Component {
render() {
const { open, list, onClose } = this.props;
return (
<Dialog
open={open}
onClose={onClose}
>
...
</Dialog>
)
}
}
SelectCatDialog.propTypes = {
open: PropTypes.bool,
onClose: PropTypes.func,
}
SelectCatDialog.defaultProps = {
list: [],
}
export default SelectCatDialog;
This is call Modal page code:
import React from 'react';
import DetailDialog from './components/DetailDialog';
import SelectProcDialog from './components/SelectProcDialog';
class Index extends React.Component {
render() {
return (
...
<DetailDialog
open={this.state.detailDialog}
entity={this.state.currentDetail}
onClose={this.handleDetailClose.bind(this)}
/>
<SelectProcDialog
open={this.state.catalogDialog}
list={this.props.catalog}
onOk={(value) => this.handleSelectCatalogOk(value)}
onClose={() => this.setState({ catalogDialog: false })}
/>
...
)
}
}
export default Index;
What happened? Working fine in v3 version. Can someone answer?
Since V4, Dialog and Modal children must be able to hold a ref.
The following can hold a ref:
Any Material-UI component
class components i.e. React.Component or React.PureComponent
DOM (or host) components e.g. div or button
React.forwardRef components
React.lazy components
React.memo components
The error declares that you provide function component as a child to modal.
To fix the error, change the function component to something that can hold a ref (e.g class component).
I just faced the exact same issue after migrating from Material-UI v3 to v4.
I totally agree with the accepted answer, nevertheless, I post here another approach, in the case someone would rather keep using a functional component (as I did).
Functional components can't hold a ref as is.
So the way of providing a ref to a child component is actually to forward it.
The React documentation has a well explained example about this, and this issue on the ReactJS Github details it a bit more.
So basically, what could be done here is :
const Transition = React.forwardRef((props, ref) => (
<Slide direction="up" {...props} ref={ref} />
));
to be able to forward the ref through the Transition component, down to the Slide component.
Typescript way
⚠️ Be aware that the previous code will throw a TS2322 error if used in a Typescript project.
This error is described and solved in this Material-UI Github issue.
So, in a Typescript project, the best way (imho) to use a functional component as Transition component for a Material-UI v4 Dialog component, is the following :
import { SlideProps } from '#material-ui/core/Slide';
const Transition = React.forwardRef<unknown, SlideProps>((props, ref) => (
<Slide direction="up" {...props} ref={ref} />
));
And the code above fixes both errors :
Failed prop type: Invalid prop children supplied to ForwardRef(Modal).
[TypeScript] Error: TransitionComponent type not assignable to 'ForwardRefExoticComponent'
I wanted to check how to react does reconciliation so I updated the inner HTML of id with the same text. Ideally, it shouldn't update the dom but it is paint reflashing in chrome.
I have tried paint reflashing in chrome it is showing green rectangle over that same text
import React from 'react';
function App() {
return (
<div >
<p id="abc" key="help">abc is here</p>
<button onClick={function () {
// document.getElementById("may").innerHTML = "";
document.getElementById("abc").innerHTML = "abc is here";
}} > Btn</button>
</div>
);
}
export default App;
Expected result should be that paint reflashing shouldn't happen but it is happening.
You are not using React here to update the text of your p tag but directly updating the DOM with JavaScript.
So React reconciliation algorithm doesn't even run here.
In React, the output HTML is a result of the state and the props of your component.
When a change in state or props is detected, React runs the render method to check if it needs to update the DOM. So, in order to do this check, you need to store the parameters that determine your view in state or props.
Given your example, we could save the text you want to show in the p tag in the state of your component (using hooks):
import React, { useState } from 'react';
function App () {
const [text, setText] = useState('abc is here');
render() {
return (
<div >
<p id="abc" key="help">{this.state.text}</p>
<button onClick={() => setText('abc is here') }>Btn</button>
</div>
);
}
}
export default App;
If you are using a version of React that does not support hooks, you will need to transform your functional component into a class to use state:
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = { text: 'abc is here' };
}
render() {
return (
<div >
<p id="abc" key="help">{this.state.text}</p>
<button onClick={() => this.setState({ text: 'abc is here' }) }>Btn</button>
</div>
);
}
}
export default App;
I have a simple React Select as content prop in a Semantic UI React Popover component. I am constrained by each of the package versions inside the project (available in the following codesandbox https://codesandbox.io/s/wy194rz908):
React: ~15.5.0
ReactDOM: ~15.5.0
React-Select: ^2.1.1
Semantic UI React: 0.71.5
As you can see, the React Select options closes when a selection is done.
On the other hand, I found that updating React, React-DOM and SemanticUI to their latest versions make the feature work. As you can see, the selection is done and the Select options do not fold (available in the following codesandbox https://codesandbox.io/s/6y14qyykk3).
As I can not update update the React and SUIR, what workaround should I follow in order to make this work?
Thanks!
You have to use a Controlled Popup Component, as stated in docs:
import React from 'react'
import { Button, Popup } from 'semantic-ui-react'
class PopupExampleContextControlled extends React.Component {
state = {}
toggle = () => this.setState({ open: !this.state.open })
handleRef = node => this.setState({ node })
render() {
const { node, open } = this.state
return (
<div>
<Button content='Open controlled Popup' onClick={this.toggle} />
<Popup context={node} content='Hello' position='top center' open={open} />
---------->
<strong ref={this.handleRef}>here</strong>
</div>
)
}
}
export default PopupExampleContextControlled
In this way you can control when popup opens and closes.
I am controlling the Popup using the open prop available through its props api. I change its state from true to false when clicking the caret down icon button.
Solution: https://codesandbox.io/s/rmoxx98qkn
Good day! I wrote this code to test the React transition group library and eventually get stuck with the error. The script gets run and I see the data fill form on the page styled but when I click submit button form does not disappear. Error reference description: Failed prop type: The prop timeout is marked as required in CSSTransition, but its value is undefined.
in CSSTransition (at app.jsx:24)
in App (at index.js:7) However transitionAppearTimeot={1500}!
import React, { Component } from 'react';
import CSSTransitionGroup from 'react-transition-group/CSSTransition';
import './app.css';
import Form from './components/Form';
class App extends Component {
constructor() {
super();
this.state = {
mounted: true,
};
this.handleSubmit = this.handleSubmit.bind(this);
};
handleSubmit(event) {
event.preventDefault();
this.setState = {
mounted: false
}
console.log(this.state);
};
render() {
return (
<div className="app">
<CSSTransitionGroup
transitionName="fade"
transitionAppear={true}
transitionAppearTimeout={1500}
transitionEnter={false}
transitionLeave={true}
transitionLeaveTimeout={300}>
{this.state.mounted && <Form onSubmit=
{this.handleSubmit} />}
</CSSTransitionGroup>
</div>
);
}
}
export default App;
The error message is very specific. <CSSTransition> requires a prop timeout but you do not pass anything. Also your other props are not what <CSSTransition> expects.
I guess you are mixing up the old react-transition-group v1 with the new react-transition-group v2. You are using v2 which has a totally different API.