I'm wondering how to make a component like what Ant design did with their message, notification, etc component.
import { message, Button } from 'antd';
const info = () => {
// How to make component / api like this?
message.info('This is a normal message');
};
ReactDOM.render(
<Button type="primary" onClick={info}>
Display normal message
</Button>,
mountNode,
);
Compared to other common library or our own, usually we need to explicitly place the component where we want the component to show.
Other framework
ReactDOM.render(
<div>
<Button type="primary" onClick={showMessage}>
Display normal message
</Button>
{/* Others need to do this */}
<Message active={messageStatus} content="Lorem ipsum" />
</div>,
mountNode
);
I checked their github, but I can't understand the code since they are using typescript and a lot of abstraction.
Also tried using React.createElement() method, but still couldn't figured out how to put the created element to the dom tree.
Related
I have a component that is only visible when a user hovers over it. In that component I have a button which allows the user to add something to the local storage. If the button is clicked the component is removed from the DOM. This works fine, but I want to show the user a toast when the action is completed. The issue is that the toast is also removed when the button is clicked because it's part of that component:
return (
<React.Fragment>
<Overlay backdrop_path={movie.backdrop_path}>
<div>
<AddMovie onClick={() => addMovie(movie)}>Add movie to your watchlist</AddMovie>
</div>
</Overlay>
<Snackbar
open={open}
onClose={handleClose}
TransitionComponent={Slide}
message="Movie has been added"
/>
</React.Fragment>
)
I rather not put the Snackbar toast in a different component because this component is responsible for adding a movie to the local storage, and I don't want to do a lot of props lifting etc to get the result.
So I thought, maybe it's possible to append the Snackbar element to the body instead of the components element. This way if the components element is removed the Snackbar should still be visible. Not sure if this logic will actually work though.
Is it possible to append a element/component to another part of the DOM structure, if so: how?
Seems appending rendered elements into other DOM elements isn't difficult:
return ReactDOM.createPortal(
<Snackbar
open={true}
onClose={handleClose}
TransitionComponent={Slide}
message="Movie has been added"
/>,
document.body
);
But this element also gets removed the instant the component that renders it is removed. Which makes sense. So it looks I need to render the Snackbar in a different element. Shame.
// edit. By using Redux it might be possible to create a global state for using the Snackbar elements > https://browntreelabs.com/snackbars-in-react-redux-and-material-ui/
// edit#2. https://github.com/iamhosseindhv/notistack this package makes it a lot easier and is supported by the Material UI team.
I'm building a tester page where the user sees a library component and documents its use.
Here is the component:
render = () => {
let component = (
<Slider
onSlide={this.handleSlide}
totalCount={120}
/>
);
return (
<div>
<h2>Slider Test:</h2>
{component}
<code>HOW TO PRINT COMPONENT CODE HERE?</code>
</div>
);
};
I want to show the component in use and at the end the code I've used to test it.
How can I put on screen the component code without the need to replicate it inside the tag?
Is there a way to do it directly or though an existing npm library?
You could escape the code and it should appear correctly.
You can use that library to do it escape-html npm
You should also be able to find specialized libraries to do it like this one react code view npm
I have an Autocomplete component returned inside the render method of a class. However, it causes nothing to render; taking the component out allows everything else to render as expected.
export default class Home extends Component {
render() {
return (
...
<Row>
<Autocomplete
id="combo-box-demo"
options={[{title: 'Inception'}, {title: 'Dark Knight'}]}
getOptionLabel={option => option.title}
style={{ width: 300 }}
renderInput={params => (
<TextField {...params} label="Combo box" variant="outlined" fullWidth />
)}
/>
</Row>
);
}
}
Taking out Autocomplete and everything inside allows the render to work normally. I am using the example Autocomplete code from Material-UI's guide.
I receive this error in the console:
*Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app*
What could be causing this render issue? What are the constraints on using Autocomplete(or hooks)?
Your code seems to work correctly.
Could you check the live demo that I have created down below?
Functional component:
https://codesandbox.io/s/material-ui-rh82u?fontsize=14&hidenavigation=1&theme=dark
Class component:
https://codesandbox.io/s/autocomplete-material-ui-tmtoj?fontsize=14&hidenavigation=1&theme=dark
I use material-ui components in react with react-router. I have a problem when I want to display list-items that are supposed to work as link elements, but also contain a submenu inside that should not trigger the parent link. It does and I don't know how to disable it.
var iconMenu =
<IconMenu iconButtonElement={<IconButton><MoreVertIcon /></IconButton>}>
<MenuItem primaryText='change name' onTouchTap={this.edit}/>
<MenuItem primaryText='delete' onTouchTap={this.delete} />
</IconMenu>
<ListItem
key={i}
containerElement={<Link to={`/items/${item.id}`} />}
rightIconButton={iconMenu}
/>
When I click the iconMenu button, I do not want the <Link to={`/items/${item.id}`} /> to be triggered, so that I stay in the page. But it does. So how can I fix this problem? I tried to add event handler to run stopPropagation() but it was not successful...
Thanks!
For React Router v4, add
onTouchTap={() => this.props.history.push(`/items/${item.id}`)}
to the ListItem, instead of containerElement.
Use import { withRouter } from 'react-router-dom' and export default withRouter(Foo) to add history to the component's props.
First of all, I'd like to admit that I do not like material-ui and thus do not recommend it to people who consider starting a project with it. The reasoning behind is that you sacrifice too much time customising the components to your needs - a solution that is opposite to the idea of React. It also uses inline styles that you always have to overwrite in the component file, not in your scss or less. This sucks big time. I don't even mention all the UI interaction actions that are handled with JS that could make your performance ache.
Another short mention is to the react-router. Unfortunately I'm not a fan of it either. Guys, why do you change the API in every next release? Why is it so damn difficult to just reset the location queries? Just look at FlowRouter and see how fantastic a route API should be implemented.
Anyways, my solution was to implement a wrapper around the <Link /> component and move the <IconMenu /> outside of the <Link /> wrapper:
<li key={i}>
<ListItem
key={i}
containerElement={<Link to={`/items/${item.id}`} />}
/>
{iconMenu}
</li>
I'd like to create a UI similar to JSFiddle using React:
A panel for HTML, CSS and JS
A preview panel
A single "Run" button that updates the preview
I'd like to use Microsoft's Monaco editor for the HTML, CSS and JS panels to get syntax highlight and autocomplete.
I've abstracted out a generic MonacoEditor component. My component hierarchy looks something like this:
<Root>
<div>HTML <MonacoEditor /></div>
<div>CSS <MonacoEditor /></div>
<div>JS <MonacoEditor /></div>
<button onClick={this.run}>Run</button>
<PreviewPanel />
</Root>
If I were implementing this UI in Vanilla JS, the run() method would call getValue() on each Monaco instance to extract the full text from each panel and generate the preview.
This becomes awkward with React, however, since the run() method can't call methods on instances of child components.
One workaround would be for the MonacoEditor to have an onUpdate prop which gets fired on every keystroke. The Root component could store the provisional contents of each panel for when the "Run" button was clicked. This is what I might do if each editor were a <textarea>. But it's a non-starter with Monaco. Serializing the text of the editor on every keystroke makes it unusably slow.
The only other approach I can think of is passing in a "getter setter" to the MonacoEditor component, e.g.:
class Root extends React.Component {
render() {
return (
<div>
<MonacoEditor setGetter={getter => this.getHTML=getter} />
<MonacoEditor setGetter={getter => this.getCSS=getter} />
<MonacoEditor setGetter={getter => this.getJS=getter} />
<button onClick={() => this.run()}>Run</button>
<PreviewPanel />
</div>
);
}
run() {
const html = this.getHTML();
const css = this.getCSS();
const js = this.getJS();
// ...
}
}
But this feels extremely awkward and counter to the idea of one-way databinding. Is there a better, more idiomatic approach?
You can checkout react-monaco-editor.
This will satisfy all your requirements! Especially getting value from the editor instance.
If not this, declare a function inside the editor component and use ref to call that function from other components.
Hope it helps!