I have this module in ./src/utils/errorPopup.js
import { Toast } from 'native-base';
export default function errorPopup(problem = 'process your request') {
Toast.show({
text: `Unfortunately we cannot ${problem}. An error report has been created and we will look into it shortly. Please get in touch with us if the problem doesn't disappear in the next 6 business hours.`,
position: 'bottom',
buttonText: 'Okay'
});
}
I then try and call it inside the componentWillMount method of ./src/components/JobList.js and it says Uncaught ReferenceError: errorPopup is not defined.
import React, { Component } from 'react';
import { View } from 'react-native';
import axios from 'axios';
import errorPopup from '../utils/errorPopup';
export default class JobsList extends Component {
constructor() {
super();
this.state = {
someObject: undefined
};
}
componentWillMount() {
axios.get('http://api.test.dev:5000/')
.then(response => {
this.setState({ someObject: response.data })
})
.catch(() => {
errorPopup('fetch your accepted and available jobs');
});
}
render() {
return (
<View />
);
}
}
Strangely enough, when I debug and use the console, I can see an object _errorPopup2.
How can I call errorPopup() without changing the design pattern of utils that I adopted from here?
Related
I'm trying to implement generic error page for any unhandled exceptions in React application. However, it seems like the error boundary doesn't work with exceptions thrown by promises such as API operations. I know I could catch the exception at component level and re-throw it at the render method. But this is boilerplate I would like to avoid. How to use error boundary with promises?
I'm using latest React with hooks and react-router for navigation.
You can do by creating a HOC which will take two parameters First one is the component and second the promise. and it will return the response and loading in the props Please find the code below for your reference.
HOC
import React, { Component } from "react";
function withErrorBoundary(WrappedComponent, Api) {
return class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false, response: null, loading: false };
}
async componentDidMount() {
try {
this.setState({
loading: true
});
const response = await Api;
this.setState({
loading: false,
response
});
console.log("response", response, Api);
} catch (error) {
// throw error here
this.setState({
loading: false
});
}
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return (
<WrappedComponent
response={this.state.response}
loading={this.state.loading}
{...this.props}
/>
);
}
};
}
export default withErrorBoundary;
How to use HOC
import ReactDOM from "react-dom";
import React, { Component } from "react";
import withErrorBoundary from "./Error";
class Todo extends Component {
render() {
console.log("this.props.response", this.props.response);
console.log("this.props.loading", this.props.loading);
return <div>hello</div>;
}
}
const Comp = withErrorBoundary(
Todo,
fetch("https://jsonplaceholder.typicode.com/todos/1").then(response =>
response.json()
)
);
ReactDOM.render(<Comp />, document.getElementById("root"));
Please check the codesandbox for your refrence
I am working on a simple react-redux project that gets information about movies from the OMDB api based on search term provided by the user. I am currently having trouble trying to get text typed into the searchbar to update the store value corresponding to the title of the film to search for. I'm fairly new to react and completely new to redux I've only finished one other redux project before and I set up my actions and reducers in the exact same way as last time but this time I'm running into "Uncaught TypeError: dispatch is not a function". This was not a problem I encountered in the previous project and my google searching has not been very helpful thus far.
I've searched this problem on google and only found a few results and none of them seem to be having the exact same issue as me, they involve using mapDispatchToProps which I'm not using inside of my connect function. Supposedly when you write a mapStateToProps like I have, dispatch should just be passed down as a prop to the connected component but whenever I try to access it I get the aforementioned "Uncaught TypeError: dispatch is not a function" error.
here is the index.js for my component
import { connect } from 'react-redux';
import MovieSearch from './MovieSearchContainer';
import {
updateSearchTerm,
getMovies
} from './movieSearchActions';
function mapStateToProps(state){
return {
title: state.movieSearch.title,
year: state.movieSearch.year,
plot: state.movieSearch.plot,
released: state.movieSearch.released,
runtime: state.movieSearch.runtime,
genre: state.movieSearch.genre,
plot: state.movieSearch.plot,
ratings: {
IMDB: state.movieSearch.ratings.IMDB,
Metascore: state.movieSearch.ratings.Metascore
},
posterUrl: state.movieSearch.posterUrl,
cachedMovies: state.movieSearch.cachedMovies
};
}
export default connect(mapStateToProps)(MovieSearch);
here is my action
export function updateSearchTerm(searchTerm){
return {
type: "UPDATE_SEARCH_TERM",
payload: { searchTerm }
}
}
here is my jsx component
import React from 'react';
import PropTypes from 'prop-types';
import {
updateSearchTerm,
getMovies
} from './movieSearchActions';
export default class MovieSearchContainer extends React.Component
{
constructor(props) {
super(props);
this.handleUpdateSearchTerm =
this.handleUpdateSearchTerm.bind(this);
}
handleUpdateSearchTerm(event){
const { dispatch } = this.props;
const { value } = event.target;
dispatch(updateSearchTerm(value));
}
render() {
return (
<div>
<h1 className='text-center'>Movie Finder</h1>
<input type='text' className='col-sm-11' id='searchBar'
onChange={ this.handleUpdateSearchTerm }/>
<button type='button' id='getMovies' className='col-sm-
1'>Go!</button>
</div>
)
}
}
MovieSearchContainer.propTypes = {
store: PropTypes.object
}
here is the reducer
export default function movieSearchReducer(state = defaultState,
action) {
const { type, payload } = action;
switch(type){
case 'UPDATE_SEARCH_TERM': {
return {
...state,
title: payload.title
}
}
default: {
return state;
}
}
}
I expect changes in the searchbar on the component on the page to be reflected in the redux store, but instead I just get this error
The dispatch prop is only available when you are directly interacting with the redux-store. When you define something like mapDispatchToProps() and pass it as the 2nd argument to connect(), dispatch, gets passed to mapDispatchToProps().
const mapDispatchToProps = (dispatch) => {
return{
actionCreator: (arg) => {
dispatch(actionCreator(arg))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Component)
If you dont want to define mapDispatchToProps(), you can effectively bind your action-creators by passing in an object to connect() as the 2nd argument. This implicitly binds dispatch to the action-creators:
import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { updateSearchTerm, getMovies } from "./movieSearchActions";
class MovieSearchContainer extends React.Component {
constructor(props) {
super(props);
this.handleUpdateSearchTerm = this.handleUpdateSearchTerm.bind(this);
}
handleUpdateSearchTerm(event) {
const { value } = event.target;
this.props.updateSearchTerm(value);
}
render() {
console.log(this.props.movies);
return (
<div>
<h1 className="text-center">Movie Finder</h1>
<input
type="text"
className="col-sm-11"
id="searchBar"
onChange={this.handleUpdateSearchTerm}
/>
<button
type="button"
id="getMovies"
className="col-sm-
1"
>
Go!
</button>
</div>
);
}
}
MovieSearchContainer.propTypes = {
store: PropTypes.object
};
const mapStateToProps = state => {
return {
title: state.movieSearch.title,
year: state.movieSearch.year,
plot: state.movieSearch.plot,
released: state.movieSearch.released,
runtime: state.movieSearch.runtime,
genre: state.movieSearch.genre,
plot: state.movieSearch.plot,
ratings: {
IMDB: state.movieSearch.ratings.IMDB,
Metascore: state.movieSearch.ratings.Metascore
},
posterUrl: state.movieSearch.posterUrl,
cachedMovies: state.movieSearch.cachedMovies
};
};
export default connect(
mapStateToProps,
{
updateSearchTerm,
getMovies
}
)(MovieSearchContainer);
With that, you do not need to explicitly call dispatch to use your action-creator. Simply use this.props.nameOfActionCreator()
See sandbox for example: https://codesandbox.io/s/simple-redux-7s1c0
I think you should connect your component inside your jsx file. Then you can access with this.props.yourFunctionToDispatch
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import {
updateSearchTerm,
getMovies
} from './movieSearchActions';
class MovieSearchContainer extends React.Component
{
constructor(props) {
super(props);
this.handleUpdateSearchTerm =
this.handleUpdateSearchTerm.bind(this);
}
handleUpdateSearchTerm(event){
const { dispatch } = this.props;
const { value } = event.target;
dispatch(updateSearchTerm(value));
}
render() {
return (
<div>
<h1 className='text-center'>Movie Finder</h1>
<input type='text' className='col-sm-11' id='searchBar'
onChange={ this.handleUpdateSearchTerm }/>
<button type='button' id='getMovies' className='col-sm-
1'>Go!</button>
</div>
)
}
}
const mapStateToProps = state => {
return {
title: state.movieSearch.title,
year: state.movieSearch.year,
plot: state.movieSearch.plot,
released: state.movieSearch.released,
runtime: state.movieSearch.runtime,
genre: state.movieSearch.genre,
plot: state.movieSearch.plot,
ratings: {
IMDB: state.movieSearch.ratings.IMDB,
Metascore: state.movieSearch.ratings.Metascore
},
posterUrl: state.movieSearch.posterUrl,
cachedMovies: state.movieSearch.cachedMovies
};
}
MovieSearchContainer.propTypes = {
store: PropTypes.object
}
export default connect(mapStateToProps, {yourFunctionToDispatch})(MovieSearchContainer);
I tried to make a network availability component for my app.
My lifecycle component in the network.js
import { Component } from 'react';
import { NetInfo } from 'react-native';
export default class Network extends Component {
constructor(props) {
super(props);
this.state = { connected: null }
}
componentWillMount() {
NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectionChange);
NetInfo.isConnected.fetch().done((isConnected) => this.setState({ connected: isConnected }))
}
componentWillUnmount() {
NetInfo.isConnected.removeEventListener('connectionChange', this.handleConnectionChange);
}
handleConnectionChange = (isConnected) => { this.setState({ connected: isConnected }) }
situation() {
if(this.state.connected)
return true
else
return false
}
}
And my main page :
import React, { Component } from 'react';
import { View, I18nManager, StatusBar, StyleSheet, Text } from 'react-native';
import { Spinner } from 'native-base';
import Network from './Network'
export default class Intro extends Component {
constructor() {
super();
I18nManager.allowRTL(true);
I18nManager.forceRTL(true);
}
render() {
var network = new Network;
alert(network.situation())
if (network==true) {
alert('online')
else
alert('offline')
}
}
But after execution, componentWillMount and componentWillUnmount are not working.
There is really no need to make React component for checking Network connection utility. You can just create a simple Network class like this and initialize/deinitialize it from your app component's lifecycles.
import { NetInfo } from 'react-native';
const NET_INFO = {};
let instance;
export default class Network {
static getInstance() {
return instance || new Network();
}
static initialize() {
NetInfo.isConnected.addEventListener('connectionChange', Network.getInstance().handleConnectionChange);
}
static deinitialize() {
NetInfo.isConnected.removeEventListener('connectionChange', Network.getInstance().handleConnectionChange);
}
handleConnectionChange = (isConnected) => {
NET_INFO.isConnected = isConnected;
}
static isInternetConnected() {
return NET_INFO.isConnected;
}
}
App component:
import React, { Component } from 'react';
import Network from './Network'
export default class Intro extends Component {
componentWillMount() {
Network.initialize();
}
componentWillUnmount() {
Network.deinitialize();
}
render() {
const connected = Network.isInternetConnected()
if (connected ==true)
alert('online')
else
alert('offline')
}
}
Because you are not using Network class as component but as a normal class.
If you want to run life-cycle methods then you need use it as Component.
like this in render method,
<Network />
and if you want to execute anything in parent for network change then use prop functions.
like this in render method,
<Network
connectivityChange={()=>{
//do your stuffs here
}}
/>
you need to call this.props.connectivityChange() in Network component when you want do something in parent.
I am getting this error:
./src/components/Playing.jsx
Line 15: 'aaa' is not defined no-undef
in my Playing.jsx:
import React, { Component } from 'react'
console.log(aaa);
From my Token.jsx
import { connect } from 'react-redux'
imports { Playing } from '../components/Playing'
const mapStateToProps = state => ({
aaa: "asdf"
})
export default connect(mapStateToProps)(Playing)
You won't be able to just console.log() anywhere in the file; it would need to be within some function of the Playing component; and it will also only be available via props, e.g.
class Playing extends React.Component {
componentDidMount() {
console.log(this.props.aaa);
}
render() {
return <span>Playing</span>;
}
}
Not sure why I'm getting this error in my simple Main.test file.
The constructor of Main.js
export class Main extends Component {
constructor(props) {
super(props);
this.state = {
location: splitString(props.location.pathname, '/dashboard/')
}
if (R.isEmpty(props.view)) {
isViewServices(this.state.location)
? this.props.gotoServicesView()
: this.props.gotoUsersView()
}
}
Main.test
import React from 'react'
import * as enzyme from 'enzyme'
import toJson from 'enzyme-to-json'
import { Main } from './Main'
import Sidebar from '../../components/Common/sidebar'
const main = enzyme.shallow(<Main />);
describe('<Main /> component', () => {
it('should render', () => {
const tree = toJson(main);
expect(tree).toMatchSnapshot();
});
it('contains the Sidebar', () => {
expect(main.find(Sidebar).length).toBe(1);
});
});
Is there a way to mock up the 'pathname'?
It seems you might have a few errors one being that in your test your not passing in any props.
And another from you accessing this.props in your constructor.
See your if statement but I'll put the fix here to be explicit
if (R.isEmpty(props.view)) {
isViewServices(this.state.location)
? props.gotoServicesView()
: props.gotoUsersView()
}
In Main.test
const location = { pathname: '/dashboard/' };
const main = enzyme.shallow(<Main location={ location }/>);