store.getState is not a function - reactjs

getting this issue that me and my peers are banging our heads against the table with. Uploaded to codesandbox to solve any issues.
rewriting the application twice, environment changes.
https://codesandbox.io/s/github/astillo/Car-Sales/tree/alexander-stillo
TypeError
store.getState is not a function
expecting the page to load.

You must remove the store prop from theApp component
<AdditionalFeatures store={props.store} />
AdditionalFeatures will fetch it anyway from the store:
const mapStateToProps = state => {
return {
store: state.store,
car: state.car
}
}
What was the issue? react-redux will use it to replace the redux store, from 7.0:
Return of store as a Prop
We've brought back the ability to pass a store as a prop directly to
connected components. This was removed in version 6 due to internal
implementation changes (components no longer subscribed to the store
directly). Some users expressed concerns that working with context in
unit tests was not sufficient. Since our components use direct
subscriptions again, we've reimplemented this option, and that should
resolve those concerns.

Related

Jest: Spy on react-redux calls (useDispatch)

I want to check if dispatch by react-redux has been called.
In debug mode, I can see that the respective line of code is executed, so dispatch is actually called but in my jest test the result is always 0 calls.
What would be the proper way to do this?
const mockDispatchFn = jest.fn();
jest.doMock('react-redux', () => ({
...jest.requireActual('react-redux'),
useDispatch: () => mockDispatchFn,
}));
...
expect(mockDispatchFn).toHaveBeenCalledWith(...);
doMock and requireActual are not really reliable especially for things that might already be caught by other modules in their constants or through function closures. Better to avoid it instead of searching how to make it working.
Option 1. Better one.
Redux docs are pretty opinionated here:
Prefer writing integration tests with everything working together. For a React app using Redux, render a <Provider> with a real store instance wrapping the components being tested. Interactions with the page being tested should use real Redux logic, with API calls mocked out so app code doesn't have to change, and assert that the UI is updated appropriately.
It means:
Real store reducer
to mock store data we will call dispatch on that story with related action creators mockedStore.dispatch(someDataLoadedAction(someMockedData))
instead of checking whether dispatch has been called we, depending on what should happen, will:
a. either validate new store state through selectors("after clicking this button in Component1 we expect Redux's selector getSomeFlag to return true") b. or validating something else happened(network call has been sent with expected parameters, document title has been changed, navigation has occured etc)
c. or validating our component has been updated according to new store's state
Something like this:
const store = createStore(reducer, applyMiddleware(thunk));
// store preparation
store.dispatch(somethingLoadedSuccess(mock1));
store.dispatch(somethingChanged(someMock2));
// rendering component
const { getByRole, getByText } = render(<Provider store={store}>
<ComopnentUnderTest prop1="a" prop2={something} />
</Provide>);
// simulating some scenario
fireEvent.click(getByRole('button', { name: 'Save' }));
// asserting
expect(someSelector1(store.getState())).toBe(42);
expect(getByText('Saved successfully')).toBeInTheDocument();
expect(mockedNetworkLayer.isDone()).toBe(true);
Option 2. Tricky and not that reliable.
Using redux-mock-store you can easily check which actions has been dispatched, however with this approach:
you cannot change Redux state during the test so will be unable to test some cases
you will need to mock store tree with static data; tests become fragile - if you ever do internal refactoring of store's structure and update correctly all the selectors, you will also need to update all the mocked static data across the tests to make them passing again; tests become unreliable - if you ever do internal refactoring of store's structure and miss to update selectors accordingly, tests will still be passing though app will be broken
// store preparation
const store = configureStore([thunk])(staticDataMockForAStore);
// rendering component
const { getByRole, getByText } = render(<Provider store={store}>
<ComopnentUnderTest prop1="a" prop2={something} />
</Provide>);
// simulating some scenario
fireEvent.click(getByRole('button', { name: 'Save' }));
// asserting
expect(store.getActions()).toContainEqual({
type: 'SOME_ACTION_TYPE',
value1: 1,
value2: 'a'
});
Also for that approach to assert on actions dispatched you may need expect.objectContaining to ignore some properties with random data(UUID etc)

How can I load only part of a Redux Store in a React Application

My tech lead has given me a challenge to engineer a way only load only parts of the store that is needed for the UI that is loaded in a single page application. This is a big data application so that is why this is important. The idea is that entire store does not need to be loaded because the amount of data.
I implemented similar recently and found How to dynamically load reducers for code splitting in a Redux application? which features a link to http://nicolasgallagher.com/redux-modules-and-code-splitting/ where Nicolas describes how they did it at Twitter.
TL;DR You want lazy-loaded reducers for this. The approach described there is to have a class as a "reducer-registry". You register your reducer/s when you need to use it/them. The registry then calls a listener with a combined reducer which includes all the currently registered reducers. You attach a listener to the registry which calls replaceReducer on your store to update it's reducer.
My implementation is here.. https://github.com/lecstor/redux-helpers/blob/master/src/reducer-registry.ts
In your mapStateToProps you can select the keys of the redux store you need in your component.
For eg.
function mapStateToProps(state) {
const { key1, key2 } = state;
const {subKey, ...restKeys} = key1;
return {
remainder: ...restKeys,
subKey,
key2,
};
}
Now this data can be accessed in the component with this.props.remainder or this.props.subKey or this.props.key2

Where should I load data from server in Redux + ReactJS?

For example I have two components - ListOfGroupsPage and GroupPage.
In ListOfGroupsPage I load list of groups from the server and store it to the state.groups
In route I have mapping like ‘group/:id’ for GroupPage
When this address is loaded, the app shows GroupPage, and here I get the data for group from state.groups (try to find group in state via id).
All works fine.
But if I reload page, I'm still on page /group/2, so GroupPage is shown. But state is empty, so the app can't find the group.
What is the proper way to load data in React + Redux? I can see this ways:
1) Load all data in root component. It will be very big overhead from traffic side
2) Don't rely on store, try to load required data on each component. It's more safe way. But I don't think that load the same data for each component - it's cool idea. Then we don't need the state - because each component will fetch the data from server
3) ??? Probably add some kind of checking in each component - first try to find required data in store. If can't - load from the server. But it requires much of logic in each component.
So, is there the best solution to fetch data from server in case of usage Redux + ReactJS?
One approach to this is to use redux-thunk to check if the data exist in the redux store and if not, send a server request to load the missing info.
Your GroupPage component will look something like
class GroupPage extends Component {
componentWillMount() {
const groupId = this.props.params.groupId
this.props.loadGroupPage(groupId);
}
...
}
And in your action...
const loadGroupPage = (groupId) => (dispatch, getState) => {
// check if data is in redux store
// assuming your state.groups is object with ids as property
const {
groups: {
[groupId]: groupPageData = false
}
} = getState();
if (!groupPageData) {
//fetch data from the server
dispatch(...)
}
}
I recommend caching the information on the client using localstorage. Persist your Redux state, or important parts of it, to localstorage on state change, and check for existing records in localstorage on load. Since the data would be on the client, it would be simple and quick to retrieve.
The way I approach this is to fetch from the server straight after the store has been created. I do this by dispatching actions. I also use thunks to set isFetching = true upon a *_REQUEST and set that back to false after a *_SUCCESS or *_FAILURE. This allows me to display the user things like a progress bar or spinner. I think you're probably overestimating the 'traffic' issue because it will be executed asynchronosly as long as you structure your components in a way that won't break if that particular part of the store is empty.
The issue you're seeing of "can't get groups of undefined" (you mentioned in a comment) is probably because you've got an object and are doing .groups on it. That object is most likely empty because it hasn't been populated. There are couple of things to consider here:
Using ternary operators in your components to check that someObject.groups isn't null; or
Detailing in the initialState for someObject.groups to be an empty array. That way if you were to do .map it would not error.
Use selectors to retrieve the list of groups and if someObject.groups is null return an empty array.
You can see an example of how I did this in a small test app. Have a look at specifically:
/src/index.js for the initial dispatch
/src/redux/modules/characters.js for the use of thunks
/src/redux/selectors/characters.js for the population of the comics, series, etc. which are used in the CharacterDetails component

Meteor handle.ready() in render() not triggering rerender of component

I have the following code in my render method:
render() {
return (
<div>
{this.props.spatulaReady.ready() ? <p>{this.props.spatula.name}</p> : <p>loading spatula</p>}
</div>
)
}
Which according to my understanding, checks if the subscriptionhandle is ready (data is there) and displays it. If no data is available, it should display a simple loading message. However, when I first load the page this snippet is on, it get's stuck on the loading part. On a page reload the data (usually) displays fine.
If I check the spatulaReady.ready() when the page first loads and while the display is stuck on 'loading spatula', and the data that should be there, the handle reports as ready and the data is there like it is supposed to be. If I refresh the page it all displays fine as well. The problem is, this way of checking for data and rendering if it has arrived has worked fine for me in the past. Is it because the render method is not reactive? Because handle.ready() should be reactive.
What makes it even weirder is that it sometimes DOES correctly display the data on page load, seemingly at random.
CreateContainer code:
export default createContainer(props => {
return {
user: Meteor.user(),
spatulaReady: Meteor.subscribe('spatula.byId', props.deviceId),
spatula: SpatulaCollection.findOne()
}
}, SpatulaConfig)
Publication code:
Meteor.publish('spatula.byId', function(deviceId) {
const ERROR_MESSAGE = 'Error while obtaining spatula by id'
if (!this.userId) //Check for login
throw new Meteor.Error('Subscription denied!')
const spatula = SpatulaCollection.findOne({_id: deviceId})
if(!spatula) //No spatula by this id
throw new Meteor.Error(403, ERROR_MESSAGE)
if(spatula.ownedBy != this.userId) //Spatula does not belong to this user
throw new Meteor.Error(403, ERROR_MESSAGE)
return SpatulaCollection.find({_id: deviceId})
})
I know I'm missing a piece of the puzzle here, but I've been unsuccessful at finding it. If you don't know the solution to my specific problem, pointing me in the right direction with another way of waiting for the data to arrive before displaying it is also greatly appreciated.
EDIT: After doing some trial-and-error and reading various other posts somewhat related to my project, I figured out the solution:
export default createContainer(props => {
const sHandle= Meteor.subscribe('spatula.byId', props.deviceId)
return {
user: Meteor.user(),
spatulaReady: sHandle.ready(),
spatula: SpatulaCollection.findOne()
}
}, SpatulaConfig)
It still makes no sense to me that moving the ready() call to create container fixed all my problems though.
As you figured out, moving the .ready() call to createContainer fixes the problem. This is because Meteor reactivity only works when you call a reactive data source (a reactive function), such as collection.find() or subscriptionHandle.ready() within a reactive context, such as Tracker.autorun or createContainer. Functions within the React component, including render, are not reactive contexts from Meteor's perspective.
Note that React and Meteor reactivity are two different things. React's reactivity works simply so that whenever a component's props or state change, it's render function is re-run. It does not understand anything about Meteor's reactive data sources. Since createContainer (that is re-run by Meteor reactivity when reactive data sources in it change) simply passes props to the underlying component, the component is re-rendered by React when the props given from createContainer change.

How to logically combine react-router and redux for client- and server-side rendering

I'd like my React based SPA to render on server side (who's not these days). Therefore I want to combine React with react-router, redux and some build layer like isomorphic starterkit.
There is hapi universal redux which joins all together, but I am struggling with how to organize my flow. My data is coming from multiple endpoints of a REST API. Different components have different data needs and should load data just in time on the client. On the server instead, all data for a specific route (set of components) has to be fetched, and the necessary components rendered to strings.
In my first approach I used redux's middleware to create async actions, which load the data, return a promise, and trigger a SOME_DATA_ARRIVED action when the promise resolves. Reducers then update my store, components re-render, all good. In principle, this works. But then I realized, that the flow becomes awkward, in the moment routing comes into play.
Some component that lists a number of data records has multiple links to filter the records. Every filtered data set should be available via it's own URL like /filter-by/:filter. So I use different <Link to={...}> components to change the URL on click and trigger the router. The router should update the store then according to the state represented by the current URL, which in turn causes a re-render of the relevant component.
That is not easy to achive. I tried componentWillUpdate first to trigger an action, which asynchronously loaded my data, populated the store and caused another re-render cycle for my component. But this does not work on the server, since only 3 lifecycle methods are supported.
So I am looking for the right way to organize this. User interactions with the app that change the apps state from the users perspective should update the URL. IMO this should make the router somehow load the necessary data, update the store, and start the reconciliation process.
So interaction -> URL change -> data fetching -> store update -> re-render.
This approach should work on the server also, since from the requested URL one should be able to determine the data to be loaded, generate initial state and pass that state into the store generation of redux. But I do not find a way to properly do that. So for me the following questions arise:
Is my approach wrong because there is something I do not understand / know yet?
Is it right to keep data loaded from REST API's in redux's store?
Is'nt it a bit awkward to have components which keep state in the redux store and others managing their state by themselfs?
Is the idea to have interaction -> URL change -> data fetching -> store update -> re-render simply wrong?
I am open for every kind of suggestion.
I did set up exactly the same thing today. What we already had, was a react-router and redux. We modularized some modules to inject things into them – and viola – it works. I used https://github.com/erikras/react-redux-universal-hot-example as a reference.
The parts:
1. router.js
We return a function (location, history, store) to set up the router using promises. routes is the route definition for the react-router containing all your components.
module.exports = function (location, history, store) {
return new Bluebird((resolve, reject) => {
Router.run(routes, location, (Handler, state) => {
const HandlerConnected = connect(_.identity)(Handler);
const component = (
<Provider store={store}>
{() => <HandlerConnected />}
</Provider>
);
resolve(component);
}).catch(console.error.bind(console));
});
};
2. store.js
You just pass the initial state to createStore(reducer, initialState). You just do this on the server and on the client. For the client you should make the state available via a script tag (ie. window.__initialstate__).
See http://rackt.github.io/redux/docs/recipes/ServerRendering.html for more information.
3. rendering on the server
Get your data, set up the initial state with that data (...data). createRouter = router.js from above. res.render is express rendering a jade template with the following
script.
window.csvistate.__initialstate__=!{initialState ? JSON.stringify(initialState) : 'null'};
...
#react-start
!= html
var initialState = { ...data };
var store = createStore(reducer, initialState);
createRouter(req.url, null, store).then(function (component) {
var html = React.renderToString(component);
res.render('community/neighbourhood', { html: html, initialState: initialState });
});
4. adapting the client
Your client can then do basically the same thing. location could be HistoryLocation from React-Router
const initialState = window.csvistate.__initialstate__;
const store = require('./store')(initialState);
router(location, null, store).then(component => {
React.render(component, document.getElementsByClassName('jsx-community-bulletinboard')[0]);
});
To answer your questions:
Your approach seems right. We do the same. One could even include the url as part of the state.
All state inside of the redux store is a good thing. This way you have one single source of truth.
We are still working out what should go where right now. Currently we request the data on componentDidMount on the server it should already be there.

Resources