mobx observer suddenly not rerendering when observable changes - javascript

My app stopped updating on observable changes and I'm going crazy trying to figure out why. The code below shows only "Counter: 5" on the screen even though the console shows that it is updating. The relevant parts of package.json are:
{
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
},
"dependencies": {
"mobx": "^6.0.4",
"mobx-react": "^7.0.5",
"react": "17.0.0",
"react-dom": "17.0.0",
"react-icons": "^4.1.0",
"react-router-dom": "^5.2.0",
"typescript": "^4.0.3",
},
"devDependencies": {
"babel-preset-mobx": "^2.0.0",
"react-app-rewire-yaml": "^1.1.0",
"react-scripts": "^4.0.1"
}
}
import React from "react";
import ReactDOM from "react-dom";
import "./index.css"
import { inject, observer, Provider } from "mobx-react";
import { observable } from "mobx";
class TestModel
{
#observable counter = 5;
start() {
setInterval(() => {this.counter++; console.log(this.counter)}, 1000);
}
}
#inject("testModel")
#observer
class TestPage extends React.Component<{testModel?: TestModel}>
{
render() {
return <div><h1>Counter: {this.props.testModel.counter}</h1></div>
}
}
const theTestModel = new TestModel();
theTestModel.start();
ReactDOM.render(
<Provider testModel={theTestModel}>
<TestPage />
</Provider>,
document.getElementById("root")
);
EDIT:
Turns out I needed to call makeObservable(this) in the constructor of my TestModel, plus I had to pull out the side effect into an action context like this:
#action incrementCounter = () => {
this.counter++;
console.log(this.counter)
}
start() {
setInterval(this.incrementCounter, 1000);
}

Since mobx#6.0.0 decorators are not enough. You have to make your class observable manually with makeObservable as well.
import { observable, makeObservable } from "mobx";
class TestModel {
#observable counter = 5;
constructor() {
makeObservable(this);
}
start() {
setInterval(() => {this.counter++; console.log(this.counter)}, 1000);
}
}

Related

(...).mockImplementation is not a function

I've used this test in the past with a React app without any issues:
import { render, fireEvent, screen, waitFor } from '#testing-library/react'
import { RelatedContent } from '../components/relatedcontent'
import { onValue } from '../components/firebase'
jest.mock('../components/firebase')
test('RelatedContent -> displays related content', async () => {
let fakeData = {
'flex-new-row': 20,
'chronlabs': 25
}
let snapshot = {val: () => fakeData}
onValue.mockImplementation((ref, callback) => {
callback(snapshot)
return jest.fn()
})
render(<RelatedContent numRelated = {5}/>)
await waitFor(() => expect(document.querySelector("a[href='/flex-new-row']")).toBeTruthy())
await waitFor(() => expect(document.querySelector("a[href='/chronlabs']")).toBeTruthy())
})
Now I'm using the same test on a Next.js app, and I'm getting the following error:
TypeError: _firebase.onValue.mockImplementation is not a function
Update
The RelatedContent component looks like:
import React, { useState, useEffect } from 'react'
import { db, onValue, ref } from './firebase'
const RelatedContent = ({ numRelated }) => {
const [related, setRelated] = useState([])
useEffect(() => {
let unsubscribe = onValue(ref(db, `posts/related`), snapshot => {
let _related = Object.keys(snapshot.val())
setRelated(_related)
})
return () => unsubscribe()
}, [])
return(
<div className = 'Related'>
{related.slice(0, numRelated).map((elem, i) =>
<Link href = {elem}>Whatever</Link>
)}
</div>
)
}
export default RelatedContent
And the Firebase component looks like:
import { initializeApp } from 'firebase/app'
import { getDatabase, goOnline, goOffline, limitToLast, onDisconnect, onValue, orderByValue, push, query, ref, remove, runTransaction, update } from 'firebase/database'
const config = { ... }
const app = initializeApp(config)
const db = getDatabase(app)
export { db, dbt, goOnline, goOffline, limitToLast, onDisconnect, onValue, orderByValue, push, query, ref, remove, runTransaction, update }
package.json looks like:
{
"name": "app-next",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "jest"
},
"dependencies": {
"#primer/octicons-react": "^16.3.0",
"chart.js": "^2.7.2",
"firebase": "^9.6.4",
"gfm": "0.0.1",
"moment": "^2.29.1",
"next": "12.0.8",
"prismjs": "^1.26.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-markdown": "^8.0.0",
"rehype-katex": "^6.0.2",
"rehype-raw": "^6.1.1",
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1"
},
"devDependencies": {
"#testing-library/jest-dom": "^5.16.1",
"#testing-library/react": "^12.1.2",
"eslint": "8.7.0",
"eslint-config-next": "12.0.8",
"eslint-plugin-promise": "^6.0.0",
"jest": "^27.4.7"
}
}
If I place the fake data and the onValue.mockImplementation() function before the test declaration, the test passes correctly.
The problem is that I've several tests with multiple definitions of the fake data, and I need to declare the fake data within every test.
If I do so, I get the error.

redux-form is not showing its all props values

i want to use all eventHandler of redux-form as show in sameple output image but its not coming as a props so i facing undefine error now what i can do to solve the issue
i have imported reducers in index.js file of src folder and create store
package.json
{
"name": "reactjs",
"version": "0.1.0",
"private": true,
"dependencies": {
"#material-ui/core": "^4.11.0",
"#material-ui/icons": "^4.9.1",
"prop-types": "^15.7.2",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"react-router-dom": "^5.2.0",
"react-scripts": "3.2.0",
"react-transition-group": "^4.4.1",
"redux-form": "^8.3.6"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}
}
reducers/index.js
import { reducer } from 'redux-form';
import { combineReducers } from "redux";
export default combineReducers({
reducer
})
TouristRegistration.js
import React, { Component } from 'react';
import { reduxForm, Field } from "redux-form";
import { Button } from "#material-ui/core"
import RegistrationField from "./ReaperComponent"
class TutiorsRegistrationForm extends Component {
handlenextpage = () => {
this.props.nextPage()
}
render() {
return (
<form>
<Field
type="text"
name="FirstName"
component={RegistrationField}
/>
<Button
color="primary"
variant="contained"
type="submit"
>Next
</Button>
</form>
);
}
}
const validate = (value) => {
const errors = {}
if (!value.FirstName) {
errors.FirstName = "Required"
}
console.log("error " + JSON.stringify(errors));
return errors;
}
export default reduxForm({
validate: validate,
form: 'TutiorsRegistrationForm'
})(TutiorsRegistrationForm);
RegistrationField.js
import React from 'react';
import { TextField } from "#material-ui/core"
const ReaperComponent = (props) => {
console.log("props " + JSON.stringify(props));
return (
<TextField />
);
}
export default ReaperComponent;
output
of instructor
output in my case
props {"input":{"name":"FirstName","value":""},"meta":{"active":false,"asyncValidating":false,"autofilled":false,"dirty":false,"form":"TutiorsRegistrationForm","invalid":false,"pristine":true,"submitting":false,"submitFailed":false,"touched":false,"valid":true,"visited":false}}

Connecting a Container component to the Redux store in ReactJs but get an error saying BrowserRouter is expecting a string but gets an object

I'm trying to hook up a Container to the redux store but I get this error:
Element type is invalid: expected a string (for built-in components)
or a class/function (for composite components) but got: object.
Check the render method of Route.
I'm trying to follow the instructions here:
https://react-redux.js.org/using-react-redux/connect-mapdispatch
My attempts are commented out at the bottom. Swapping either for the normal export default statement produces this error but the normal way works fine so I know this is a problem with connect.
My container:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import axios from 'axios';
//import { bindActionCreators } from 'redux'
import queryString from 'query-string';
import { signIn } from "../actions";
class MomentumContainer extends Component {
state = {display: "Waiting"};
componentWillMount() {
const query = queryString.parse(this.props.location.search);
console.log(`query: ${JSON.stringify(query)}`);
if(query.user_id) {
console.log(`User logged in w/ id: ${query.user_id}`);
//this.props.dispatch(signIn(query.user_id));
}
}
async componentDidMount() {
const dev = process.env.REACT_APP_DEV_SERVER_ENDPOINT;
const prod = process.env.REACT_APP_PROD_SERVER_ENDPOINT;
const base_endpoint = process.env.REACT_APP_DEV_MODE ? dev : prod;
const api_endpoint = `${base_endpoint}momentum`;
//TODO above can be handled on app startup and managed by redux??
//console.log(api_endpoint);
const response = await axios.get(api_endpoint);
this.setState({display: response.data});
}
render() {
return <h1>{this.state.display}</h1>
}
}
// // Tried this
// const actionCreators = { signIn };
// export default connect(null, actionCreators)(MomentumContainer);
// // Tried this
//export default connect()(MomentumContainer);
// This works
export default MomentumContainer;
My index.js file:
import React from "react";
import ReactDOM from "react-dom";
import {Provider} from "react-redux";
import {createStore, compose, applyMiddleware} from "redux";
import thunk from "redux-thunk";
import promise from 'redux-promise-middleware';
import logger from "redux-logger";
import registerServiceWorker from "./registerServiceWorker";
import "./index.css";
import App from "./App";
import {BrowserRouter, Route, Switch} from "react-router-dom";
import reducers from './reducers';
import MomentumContainer from "./containers/MomentumContainer";
const { REACT_APP_DEV_MODE } = process.env;
const dev_mode = JSON.stringify(REACT_APP_DEV_MODE) === JSON.stringify("true");
const middleware = applyMiddleware(promise, thunk, logger);
let store_dev;
if (dev_mode === true) {
console.log('App starting in development mode.');
const allStoreEnhancers = compose(
middleware,
window.devToolsExtension && window.devToolsExtension()
);
store_dev = createStore(reducers, {}, allStoreEnhancers)
} else {
console.log('App starting in production mode');
}
const store_production = createStore(reducers, {}, middleware);
const store = dev_mode ? store_dev : store_production;
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<Switch>
<Route path="/" exact component={App} />
<Route path="/Momentum" exact component={MomentumContainer} />
</Switch>
</BrowserRouter>
</Provider>,
document.getElementById("root")
);
registerServiceWorker();
My package.json:
{
"name": "ticker-alert-frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"#material-ui/core": "^3.9.3",
"ajv": "^6.10.0",
"axios": "^0.18.0",
"materialize-css": "^1.0.0-rc.2",
"query-string": "^6.1.0",
"react": "^16.8.6",
"react-dom": "^16.4.0",
"react-redux": "^7.0.3",
"react-router-dom": "^4.3.1",
"react-scripts": "1.1.4",
"redux": "^4.0.1",
"redux-logger": "^3.0.6",
"redux-promise": "^0.6.0",
"redux-promise-middleware": "^6.1.0",
"redux-thunk": "^2.3.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
Any help/suggestions would be greatly appreciated!
After reading here (https://github.com/ReactTraining/react-router/issues/4354) about a similar issue, and seeing a suggestion to update react-router-dom, I tried updating that package to version 5.0.0 from 4.3.1 and it didn't work.
After updating the rest of my packages by running npm update --save my app is working. If anyone is having a similar problem I recommend running this command.

Redux action creator getting "TypeError: dispatch(...) is not a function" (code attached)?

Any ideas why I am getting the following this "dispatch is not a function" error in my listEventsActionCreator function when it calls "dispatch(listEventsRequestedAction())" ???
If I comment out the lines in this method after the dispatch it actually then works which is strange.
Using react-create-app, with redux, typescript, thunk.
ERROR:
TypeError: dispatch(...) is not a function
CODE:
export function listEventsRequestedAction() {
return {
type: PlannerEventTypes.LIST_EVENTS_REQUESTED
}
}
export const listEventsReceivedAction = (events:PlannerEvent[]) => {
return {
type: PlannerEventTypes.LIST_EVENTS_RECEIVED,
events
}
}
export const listEventsErrorAction = (err:any) => {
return {
type: PlannerEventTypes.LIST_EVENTS_ERROR,
error: err
}
}
export const listEventsActionCreator = () => {
return (dispatch: any) => {
dispatch(listEventsRequestedAction()) // <== ERROR: TypeError: dispatch(...) is not a function
(API.graphql(graphqlOperation(listEvents)) as Promise<any>).then((results:any) => {
const events = results.data.listEvents.items
dispatch(listEventsReceivedAction(events))
}).catch((err:any) => {
// console.log("ERROR")
dispatch(listEventsErrorAction(err))
})
}
}
package.json
{
"name": "planner",
"version": "0.1.0",
"private": true,
"dependencies": {
"#types/graphql": "^14.0.7",
"#types/react-redux": "^6.0.10",
"aws-amplify": "^1.1.19",
"aws-amplify-react": "^2.3.0",
"date-fns": "^1.30.1",
"eslint": "^5.9.0",
"konva": "^2.5.1",
"moment": "^2.22.2",
"moment-timezone": "^0.5.23",
"react": "^16.6.3",
"react-dom": "^16.6.3",
"react-draggable": "^3.0.5",
"react-konva": "^16.6.31",
"react-moment": "^0.8.4",
"react-redux": "^5.1.1",
"react-scripts-ts": "3.1.0",
"redux": "^4.0.1",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0"
},
"scripts": {
"start": "react-scripts-ts start",
"build": "react-scripts-ts build",
"test": "react-scripts-ts test --env=jsdom",
"eject": "react-scripts-ts eject"
},
"devDependencies": {
"#types/jest": "^23.3.10",
"#types/node": "^10.12.10",
"#types/react": "^16.7.10",
"#types/react-dom": "^16.0.11",
"#types/redux-logger": "^3.0.7",
"typescript": "^3.2.1"
}
}
index.tsx
import * as React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { applyMiddleware, createStore } from 'redux'
import App from './App'
import './index.css'
import rootReducer from './reducers/index'
import registerServiceWorker from './registerServiceWorker'
import { createLogger } from 'redux-logger'
import thunkMiddleware from 'redux-thunk'
const loggerMiddleware = createLogger()
const store = createStore (
rootReducer,
applyMiddleware(
thunkMiddleware, // lets us dispatch() functions
loggerMiddleware // neat middleware that logs actions
)
)
render(
<Provider store={store}>
<App testStr='test' />
</Provider>
,document.getElementById('root') as HTMLElement
);
registerServiceWorker();
The problem is because of the syntax that you followed. You don't have a semicolon after dispatch(listEventsRequestedAction()) and since you follow it up with (API.graphql(graphqlOperation(listEvents) as Promise<any>) while compiling it is evaluted as dispatch(listEventsRequestedAction())(API.graphql(graphqlOperation(listEvents) as Promise<any>) and hence it gives you the error.
Adding a semicolon after dispatch(listEventsRequestedAction()) would work
export const listEventsActionCreator = () => {
return (dispatch: any) => {
dispatch(listEventsRequestedAction());
(API.graphql(graphqlOperation(listEvents)) as Promise<any>).then((results:any) => {
const events = results.data.listEvents.items
dispatch(listEventsReceivedAction(events))
}).catch((err:any) => {
// console.log("ERROR")
dispatch(listEventsErrorAction(err))
})
}
}
P.S. The semicolon is not required in Javascript and would not cause a
js error on its own. It could however cause an error if concatenating
with other scripts.

React + Redux: "<Provider> does not support changing `store` on the fly"

I'm getting this error:
<Provider> does not support changing store on the fly. It is most likely that you see this error because you updated to Redux 2.x and React Redux 2.x which no longer hot reload reducers automatically. See https://github.com/reactjs/react-redux/releases/tag/v2.0.0 for the migration instructions.
I have a component with this:
import React, { Component } from 'react';
import {
AppRegistry,
NativeAppEventEmitter
} from 'react-native';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducers from './src/reducers';
import Onboarding from "./src/onboarding/Onboarding";
import Home from "./src/home/Home";
import codePush from 'react-native-code-push';
class Edmund extends Component {
...
startScreen() {
if (this.state.screen === "HOME" ) {
return (<Home />);
}
return (
<Onboarding />
);
}
render() {
return (
<Provider store={ createStore(reducers) }>
{ this.startScreen() }
</Provider>
)
}
AppRegistry.registerComponent('Edmund', () => Edmund)
My src/reducers/index.js file:
import { combineReducers } from 'redux';
export default combineReducers({
libraries: () => []
});
My packages:
{
"name": "Indigo",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"dependencies": {
"lodash": "^4.13.1",
"react": "^15.2.0",
"react-native": "^0.27.1",
"react-native-apple-pay": "0.0.0",
"react-native-code-push": "^1.11.0-beta",
"react-native-loading-spinner-overlay": "^0.3.0",
"react-native-navigation": "^1.0.2",
"react-native-paged-scroll-view": "^2.0.1",
"react-native-progress": "^3.0.1",
"react-redux": "latest",
"redux": "^3.0.0",
"underscore": "^1.8.3"
},
"devDependencies": {
"eslint": "latest",
"eslint-config-airbnb": "latest",
"eslint-plugin-import": "^1.16.0",
"eslint-plugin-jsx-a11y": "latest",
"eslint-plugin-react": "latest"
}
}
I'm not even doing anything fancy so I don't get why there's this error. Can anyone help?
If you dig into the react-redux code a bit you see this
if (store !== nextStore) {
warnAboutReceivingStore()
}
So it would seem all you would have to do is move the createStore call outside.
store = createStore(reducers)
class Edmund extends Component {
...
startScreen() {
if (this.state.screen === "HOME" ) {
return (<Home />);
}
return (
<Onboarding />
);
}
render() {
return (
<Provider store={ store }>
{ this.startScreen() }
</Provider>
)
}
Haven't tested it but should work.
I got the same error and the workaround is to remove the DevTools.
<Provider store={store} >
<App />
</Provider>
In the component App's render() method, there is a:
<DevTools />
After I removed the DevTools, the error disappeared.
This is not a good solution, just for your reference.

Categories