I'm new to reactjs, and I've just started using state management tool Mobx.
I'd like to show Snackbar using mobx store.
The logic is simple.
When the error was occured, it was added to mobx's observable alert list.
And, the observer component catched that change, and rebuild component.
Here is my code.
// BaseAlert.ts
interface BaseAlert {
message: string;
display: boolean;
}
export interface Success extends BaseAlert {
kind: 'success';
}
export interface Info extends BaseAlert {
kind: 'info';
}
export interface Warn extends BaseAlert {
kind: 'warn';
}
export interface Error extends BaseAlert {
kind: 'error';
}
export type Alert = Success | Info | Warn | Error;
export const alert = (message: string, kind: Alert['kind']): Alert =>
({
message,
kind,
display: true,
} as Alert);
// Alerts.ts
import {just, Maybe, nothing} from 'maybeasy';
import {action, computed, observable} from 'mobx';
import {Alert} from "../base/BaseAlert";
class AlertsStore {
#observable
alerts: Alert[] = [];
#computed
get current(): Maybe<Alert> {
return this.alerts.length === 0 ? nothing() : just(this.alerts[0]);
}
#action
hide = () => {
if (this.alerts.length > 0) {
this.alerts[0].display = false;
}
};
#action
process = () => {
this.alerts = this.alerts.slice(1);
};
#action
push = (alert: Alert) => {
this.hide();
this.alerts.push({...alert, display: true});
};
}
const alertsStore = new AlertsStore();
export default alertsStore;
// ObservableAlert.tsx
import React from 'react';
import alertsStore from "../../hook/AlertsStore";
import {Snackbar} from "#mui/material";
import {observer} from "mobx-react";
#observer
export class Alerts extends React.Component {
render() {
return alertsStore.current.cata({
Just: alert => (
<Snackbar
anchorOrigin={{vertical: 'top', horizontal: 'right'}}
open={alert.display}
onClose={alertsStore.hide}
onDragExit={alertsStore.process}
message={alert.message}
autoHideDuration={1000}
/>
),
Nothing: () => <></>,
});
}
}
And, I added the ObservableAlert component just below the App Component.
// App.tsx
function App() {
return (
<div className="App">
<Router>
<Routes>
<Route path="/*" element={<HomeView/>}>home</Route>
<Route path="/login" element={<LoginView/>}>login</Route>
</Routes>
</Router>
<Alerts/>
</div>
);
}
When I input username and password in textfield and click the login button, this function is called to push alert into the alertsStore.
async login(username: string, password: string) {
await this.baseEventHandle({
action: async () => {
await this.loginUserUseCase.invoke(username, password);
},
onError: (error) => {
alertsStore.push(alert(error, 'error'));
}
})
}
I checked above function's onError inner function was worked and new Alerts Instance was added to AlertsStore's Alert list.
But, the snackbar did not show at the browser.
Could you tell me what is the problem of my code??
Related
I have my react app created from vite and there I have my Custom React Error Boundary Component wrap from Components the thing is it cannot catch errors.i debug my error component but it cannot recieve any value in getDerivedStateFromError not componentDidCatch
Here is my Error Boundary Code:
/* eslint-disable #typescript-eslint/no-unused-vars */
import React, { Component } from 'react';
interface IState {
hasError: boolean;
eventId?: string;
}
// eslint-disable-next-line #typescript-eslint/no-empty-interface
interface IProps {
children: any;
}
export default class ErrorBoundary extends Component<IProps, IState> {
constructor(props: Readonly<{}>) {
super(props);
this.state = { eventId: '', hasError: false };
}
static getDerivedStateFromError(error: any) {
console.log('here get Derived');
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error: any, errorInfo: any) {
console.log('My errors', error);
}
render() {
// const { children } = this.props;
console.log('errors');
if (this.state.hasError) {
console.log('errors found', this.state.hasError);
return (
<button
onClick={() =>
console.log("Error Found)
}
>
Report feedback
</button>
);
}
return this.props.children;
}
}
and my app.js code:
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<ErrorBoundary>
<button
onClick={() => {
throw new Error('Im new Error');
}}
>
Click Me
</button>
</ErrorBoundary>
</header>
</div>
);
}
export default App;
does anyone knows what is the issue ?
Error boundaries do not catch errors for:
Event handlers
Asynchronous code (e.g. setTimeout or requestAnimationFrame
callbacks)
Server side rendering
Errors thrown in the error boundary itself
(rather than its children)
https://reactjs.org/docs/error-boundaries.html#introducing-error-boundaries
To simulate an error, you need to create a component, make it a child of ErrorBoundary class and click on the button 2 times
function Button() {
const [count, setCount] = useState(0);
const onClick = () => {
setCount(count + 1);
};
useEffect(() => {
if (count === 2) {
throw new Error('I crashed!');
}
});
return (
<button
onClick={onClick}
>
Click Me
</button>
);
}
export default Button;
Below is the code of the Service
import { fetchEventSource } from '#microsoft/fetch-event-source';
export const AlertFetchEventSource = () => {
fetchEventSource('https://puppygifs.tumblr.com/api/read/json'),
{
onmessage(ev) {
const data = JSON.parse(ev.data);
return data;
},
};
};
export default { AlertFetchEventSource };
index.tsx where I am making the call to this service but its not returning any data
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
import backendService from './services/backendService';
interface AppProps {}
interface AppState {
name: string;
}
class App extends Component<AppProps, AppState> {
constructor(props) {
super(props);
this.state = {
name: 'React',
};
console.log(backendService.AlertFetchEventSource());
}
render() {
return (
<div>
<Hello name={this.state.name} />
<p>Start editing to see some magic happen :)</p>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Given the library you're using is an event handler, it doesn't ever actually return data. You'd need to provide it a callback to execute when it receives events.
// you might want to specify a better type than `any`
type MessageHandler = (data: any) => void;
export const AlertFetchEventSource = (onEvent: MessageHandler) => {
fetchEventSource('https://puppygifs.tumblr.com/api/read/json'), {
onmessage(ev) {
const data = JSON.parse(ev.data);
onEvent(data);
},
}); // you had a typo here, missing ")"
};
and use it like
import { AlertFetchEventSource } from "./services/backendService";
// snip
AlertFetchEventSource(data => {
// handle event data, eg
console.log(data);
// or perhaps something like
this.setState(data);
});
I am super stuck with ReactJs in trying to add one Virtual Component to another Component at runtime and failing to do so. Here is what I am trying to do:
My App.js looks like this:
import React from 'react';
import './App.css';
// Components
import Header from './components/Header';
import Footer from './components/Footer';
import LeftSideSpace from './components/LeftSideSpace';
import RightSideSpace from './components/RightSideSpace';
import CenterSpace from './components/CenterSpace';
// main class name: App
class App extends React.Component {
// main function name: render
render() {
return (
<div className="App">
<Header title='My Blog'/>
<LeftSideSpace/>
<CenterSpace/>
<RightSideSpace/>
<Footer title='Welcome! This is my Blog site'/>
</div>
);
}
}
export default App;
My focus is on the component <CenterSpace/> which I am importing from here:
import React from 'react';
import PropTypes from 'prop-types'
class CenterSpace extends React.Component {
render() {
return (
<centerspace className="Site.CenterSpace">
<div id="Site.CenterSpace.Content">
{this.props.children}
</div>
</centerspace>
);
}
}
// props defaults
CenterSpace.defaultProps = {
title: 'Personal Blogger\'s site'
}
// props validations
CenterSpace.propTypes = {
title: PropTypes.string.isRequired
}
export default CenterSpace
Then I have a menu component like this, as of now, this is what I have in code, which I am sure contains bugs:
import React from 'react';
import PropTypes from 'prop-types'
import CenterSpace from '../CenterSpace'
import HomeLists from './HomeLists'
class MainMenu extends React.Component {
render() {
return (
<div className="Site.MainMenu">
<button onClick={this.props.onClickHome}>Home</button>
<button onClick={this.props.onClickBlogs}>Blogs</button>
<button onClick={this.props.onClickAboutMe}>About Me</button>
</div>
);
}
}
// props defaults
MainMenu.defaultProps = {
//control button clicks
onClickHome: () => {
var home_dom = new HomeLists();
var center_dom = new CenterSpace<String>("My Blog list");
console.log("say we went to home")
center_dom.appendChild(home_dom);
},
onClickBlogs:() => {
console.log("say we went to blogs")
},
onClickAboutMe:() => {
console.log("say we went to about me")
}
}
// props validations
MainMenu.propTypes = {
onClickHome: PropTypes.func.isRequired,
onClickBlogs: PropTypes.func.isRequired,
onClickAboutMe: PropTypes.func.isRequired,
}
export default MainMenu
This main-menu is used to dynamically add and remove components, but I am failing to do so. When I click Home button, the action I am trying achieveis to add <HomeList/> component to <CenterSpace/>. And futher, <HomeList/> is parsing some Json files and appending as child divs.
<HomeList/> looks like this (may have some issues, was not able to make it work, but that is something I can fix):
import React from 'react';
import PropTypes from 'prop-types'
class HomeLists extends React.Component {
render() {
const fs_obj = require('fs');
const fs_path = require('path');
const fs_jsons = fs_obj.readdirSync('../data').filter(file => fs_path.extname(file) === '.json');
fs_jsons.forEach(file => {
const file_data = fs_obj.readFileSync(fs_path.join('../data', file));
const json = JSON.parse(file_data.toString());
const blog_title = json.title;
var snippet_header = document.createElement('h3');
snippet_header.textContent(blog_title);
const blog_desp = json.blog.content[0].value;
var snippet_text = document.createElement('p');
snippet_text.textContent(blog_desp);
var snippet = document.createElement('div');
snippet.appendChild(snippet_header);
snippet.appendChild(snippet_text);
this.appendChild(snippet);
});
return (
<homelists className="Site.HomeLists">
<div id="Site.HomeLists.Content">{HomeLists}</div>
</homelists>
);
}
}
// props defaults
HomeLists.defaultProps = {
title: 'Personal Blogger\'s site'
}
// props validations
HomeLists.propTypes = {
title: PropTypes.string.isRequired
}
export default HomeLists
Right now when I click Home, all I get is the following error:
TypeError: center_dom.appendChild is not a function
onClickHome
src/components/complications/MainMenu.js:29
28 | console.log("say we went to home")
> 29 | center_dom.appendChild(home_dom);
| ^
30 | },
31 | onClickBlogs:() => {
32 |
console.log("say we went to blogs")
Can anyone help me get unblock from here.
Use the following component as an example for conditional rendering and it is based on your question as well.
import React from "react";
class MainMenu extends React.Component {
constructor(props) {
super(props);
this.state = { isHome: false, isBlogs: false, isAboutMe: false };
// Binding this keyword
this.onClickHome = this.onClickHome.bind(this);
this.onClickBlogs = this.onClickBlogs.bind(this);
this.onClickAboutMe = this.onClickAboutMe.bind(this);
}
onClickHome() {
this.setState({ isHome: true, isBlogs: false, isAboutMe: false });
}
onClickBlogs() {
this.setState({ isHome: false, isBlogs: true, isAboutMe: false });
}
onClickAboutMe() {
this.setState({ isHome: false, isBlogs: false, isAboutMe: true });
}
render() {
return (
<div className="Site.MainMenu">
<button onClick={this.onClickHome}>Home</button>
<button onClick={this.onClickBlogs}>Blogs</button>
<button onClick={this.onClickAboutMe}>About Me</button>
{this.state.isHome && <div>Home view is enabled</div>}
{this.state.isBlogs && <div>Blogs view is enabled</div>}
{this.state.isAboutMe && <div>AboutMe view is enabled</div>}
</div>
);
}
}
export default MainMenu;
Application View
Refer this link for more info on conditional rendering: https://reactjs.org/docs/conditional-rendering.html
enter image description here
When I tried to destructure the notification_menu value am getting the error
like this
am using Redux for state management
enter image description here
import React, { Suspense,Component } from 'react';
import { Switch, Route } from "react-router-dom";
import { connect } from 'react-redux';
import { NotificationMenu } from "./Redux/NotificationMenu/nof_selector";
class App extends Component {
state = {
navOpen: false,
};
NavCall = () => {
this.setState({ navOpen: !this.state.navOpen });
};
render() {
console.log(this.props.notification_menu);
const { navOpen } = this.state;
const NavOpenStyleMargin = navOpen ? { marginLeft: "250px" } : {};
return (
<div>
</div>
)
}
}
const mapStateToProps = (state:any) => {
return {
// userID: selectCurrentUser(state),
// account_menu: AccountMenu(state),
notification_menu: NotificationMenu(state),
};
};
export default connect(mapStateToProps,null)(App);
when using typescript you need to declare props and state types to your class component like:
interface IProps {
notification_menu: string // define what type notification_menu is
}
interface IState {
navOpen: boolean
}
class MyComponent extends React.Component<IProps, IState> {
// code here
}
I have a react component which shows a message:
Here's the code:
import React, { PropTypes } from 'react';
const Message = props => {
const { type, msg } = props;
if (type === 'success') {
return (<div>{msg}</div>);
} else {
return null;
}
};
Message.PropTypes = {
type: PropTypes.string.isRequired,
msg: PropTypes.string.isRequired,
};
export default Message;
//This component is called like this from index.js:
<Message type="success" msg="This is a Message" />
My question is...How can I call the component like I would call a function.
For example:
if (function is success then) {
//Show the Message Component
}
How can I do this with React?
If the if clause is within another React component you'd just render it,
class AnotherReact extends Component {
render() {
let alert = success ? <Message /> else '';
return (<div>{ alert }</div>);
}
}
Otherwise if not in a React component then you would have to use ReactDOM.render().
if (is success) {
ReactDOM.render(<Message />, document.querySelector());
}