Redux state isn't being updated across the app in development - reactjs

I'm having a very weird issue with redux. In my current app, I decided to use a logout pattern suggested by Dan Abramov.
Basically, it looks like this:
const withLogout = (state, action) => {
if (action.type === UserActions.logout.toString()) {
state = undefined
}
return rootReducer(state, action)
}
The state is getting purged after the logout action is dispatched. I added logs to reducers and they all show that the state is getting reset to a default value. React Native Debugger shows the same thing.
However, the state inside the connect(react-redux) functions doesn't change. Even weirder, it looks like the function detects the fact that the state has changed because the logger function in mapState is being triggered. But the logger's output shows that the state hasn't changed there. Components are also not getting updated.
And as I mentioned in the title this issue happens only in dev mode. As soon as I build the app and install it everything starts working as intended. And it isn't caused by Debugger.
Any thoughts?

I found the reason. I had to import the store in a couple of places where I couldn't use react-redux to dispatch the action. This led to require cycles which sometimes led to creating multiple instances of the store.
I stumbled upon the following solution that makes sure you're using the same instance of the store across the app: instead of importing store in several places use the following pattern:
// somewhere in the code
let store
export const setStore = storeInstance => store = storeInstance
...
store.dispatch()
// store index.js
import { setStore } from 'someplace'
...
setStore(store)

Related

Devtools for React Easy State?

i just switcher from redux, is there any tooling available, to inspect or even manipulate the react Easy State stores for dev purpose or do you have any good practice Tipps to do so?
Is ist maybe possible to console.log the current State on every change?
We don't have a devtool yet but it is an often requested feature. It's on our agenda and we are already collecting data about what people expect from a devtool. Sooo... what are the must-have features in a React state devtool to you?
About the timeline: we will release better docs, a linter, and probably strict-mode before a devtool. We already have a very basic devtool primitive (which just logs a lot of data) that could be used in the meantime. It would never be an official API though and we would just remove it in a later release. Are you interested? Should we release it as a temporary solution?
Is ist maybe possible to console.log the current State on every change?
Sure:
import { store, autoEffect } from '#risingstack/react-easy-state'
const myStore = store({
name: 'Bob'
})
autoEffect(() => console.log(JSON.stringify(myStore, null, 2)))
// logs `{ name: 'Ann' }`
myStore.name = 'Ann'
(I am an author of React Easy State)
If you're using global stores, e.g.:
const myStore = store({
a: 1
});
You can assign them to the window object so in your chrome/firefox devtools you could do something like:
window.__EASY_STORES__ = {
MY_STORE: myStore
}
You can then mutate that object in the console and it should be reflected in the rendering if your components are wrapped in view.
Other than that there's currently discussion around building a whole suite of devtools in the community, but at the moment we don't provide any out of the box inspector or dev tooling around the library.
It is indeed possible to log state changes using middleware. A simple logging middleware can look like this (it's typed in Flow):
export default (store: Store<ReduxState>) => (
next: (action: Action) => ReduxState
) => (action: Action) => {
console.log(`dispatching: ${action.type}`)
const result = next(action)
console.log(`next state: ${JSON.stringify(store.getState())}`)
return result
}
Manipulating is another thing. You could eather create a "cli" - I've recently done this in a project. It's basically just a JS function exposed to the browsers console.
Or, what I would suggest is using a browser plugin. The most commonly known is probably "Redux DevTools" which is available at least for Firefox and Chrome. It gives you CRUD-control (create, read, update, delete) over redux-state.
Edit: Since I fataly misread your question, this comment on GitHub might interest you. Seems not to have very active maintainers^^ But sorry, I don't know anything about easy-state.

When 'action'/'runInAction' is really needed in mobx react

Can someone explain to me what is the real difference and why both of the example here are working the same:
1) Change a state of observable via action/runInAction inside the store file:
colorStore file:
#observable
color='red'
#action
setColor(){
this.color='blue'
}
2)Change the state via the component itself (which assumed to be bad practice):
React Component file:
onClick = () => this.props.colorStore.color='blue' //still working...
Mobx action is doing the batching, similarly to how ReactJS batches multiple changes.
When you use reactjs click handler react is automatically batching changes that happen inside it, so you will not see component rendering multiple times, however, if you call setColor from some other event, let's say after loading some data, and have multiple calls to change the observable inside the setColor that will trigger the observer three times, and the component will be rendered three times.
When you wrap your function with #action decorator or you use runInAction function, only the last value will be used (green in the code below) and the component will be rendered only once.
setColor(){
// this will render three times
this.color='blue'
this.color='red'
this.color='green'
}
vanilla mobx example that reacts only once:
import { runInAction, observable, reaction, toJS } from "mobx";
const test = observable({
name: "sonic",
nick: "speed demon"
});
// console.log will be called only once with the value "name: 3"
reaction(
() => toJS(test),
data => console.log(`name: ${data.name}`)
);
runInAction(() => {
test.name = "1";
test.name = "2";
test.name = "3";
});
view on codesandbox
Also check out the discussion on the github repo: is #action really necessary?
The difference is more related to conventions and writing clean maintainable code than in the behavior of the program. So:
You are mutating store data in your UI component. This is not correct, because the UI should only visualize data and handle for example user actions which are then translated to certain action in the data store(in your case updating a color). It's store responsibility to manage the state of this data
All observable data is considered a good practice to be mutated only in actions. Why? Because the single source of mutating data is clear - only in actions and only in a data layer(the store). The code is less prone to errors and unclear state management and the great benefit is that you can enforce your application not to build if you turn on use strict mode

Redux Offline issue with rehydrating reducer to null

I am using redux-offline, below is how I am creating my store
let customConfig = {
...offlineConfig,
persistOptions:{
key: 'root',
transforms: [immutableTransform()],
},
returnPromises : false,
persistCallback : () => {this.setState({rehydrated : true})}
};
const { middleware, enhanceReducer, enhanceStore } = createOffline(customConfig);
let middlewares = applyMiddleware(offlineCommitHandler,thunk,middleware,);
store = createStore(enhanceReducer(IndexReducer),undefined,compose(enhanceStore,middlewares,persistAutoRehydrate({log:true})));
I have multiple reducers.
The issue occurs only in rehydrating one reducer, for ex : reducerA
I placed a debugger in autoRehydrate
When opening the app first time it merges the data for reducerA
When opening the app second time inbound state for reducerA is
null.
I never did offline config but I ran into similiar problems with redux-persist.
So first of all you want to make sure that your debugger and your device/emulator are not drifting away in time. This would cause me problems all the time, simply restarting the emulator and chrome tab which I was using for debugging would solve it.
Second, you want to check if you don't have any errors in your reducer. In my case, I was not following the immutability pattern and that would sometimes cause redux not to persist my state, as it did not see any difference between the old state and the new state.
For anyone looking for answer to similar issue.
My issue was that the data i was trying to store was hitting the max storage limit for asyncStorage .
https://github.com/rt2zz/redux-persist/issues/199
I had to shift to using filestorage instead of asyncStorage to get redux persist store the data

Sending error message from reducer to user

I'm new to React and Redux and I'm trying to write a simple application where a person can submit a URL for an image and it will show up on the page. Note that there is no backend to the application as of yet.
export const addImage = (url) => {
return {
type: ADD_IMAGE,
key: Guid.create().toString(),
payload: url
}
}
Adding an image creates an action of type ADD_IMAGE and my reducer updates the state consequently. However I also check if the URL is already in the list.
switch (action.type) {
case ADD_IMAGE:
if (state.find(image => image.url === action.payload)) {
return state;
} else {
return(
[
...state,
{key: action.key, url: action.payload}
]
);
}
break;
default:
}
The problem is that when I deny a post because the URL is already in the state I also want to convey that message to the user by showing it in a div next to the form. From what I've read I think I'm not supposed to try to access React state from reducers (if that is even possible) and... well.. I'm just stuck. I've been trying to find a simple guide on how to do this but I find nothing I can quite understand. After adding a database I guess I will have to do this as part of the async process but as I have it now I guess there should be some kind of simple solution.
You are starting to introduce logic into your reducer and this will inevitably lead to situation where you need to process some state outside of the reducer's scope.
The solution is to transfer your reducer logic into a thunk using a middleware package such redux-thunk (or similar package). This allows you to treat special kinds of actions as functions which means you can extend a plain action with specific action-related logic. The example you give of needing to dispatch an error action under certain conditions is an excellent use-case for redux-thunk.
Below is a example of how you might pull the logic out of your reducer into a thunk. You should note that, unlike reducers, thunks explicitly support fetching state and dispatching subsequent actions via the getState and dispatch functions.
Thunk example
export const addImage = (url) => {
return (dispatch, getState) => {
const key = Guid.create().toString()
dispatch({
type: ADD_IMAGE,
key,
payload: url
})
const state = getState()
// you would want to use a `selector` here to locate the existing image
// within the state tree
const exists = selectors.images.exists(state, url)
if (exists) {
dispatch(actions.ERROR_IMAGE_EXISTS({key, url}))
}
}
}
A note on selectors
You will see that I am using a selector to determine if the image exists. In the same way that thunks are the place to put your dispatch logic, a selector is the place to put your state-traversal logic. They are used to return portions of the state-tree or provide simple state-utilities such as the exists function shown above. Packages are available to help, for example reselect.
Follow on questions from comments
Are selectors not a built-in thing in Redux?
No they are not. Selectors are an idea that builds on top of redux and the concept exists as a place to put your state searching, caching, reading logic. This extracts the sometimes complex state traversal logic out of your thunks and components and into a nice tidy, structured collection of selectors.
Why use a selector instead of state.images.find(i => i.url === url)?
If you use a selector package then you get far more benefit than just a good separation of concerns, you get a big performance improvement (see usage example below).
Here are the headlines from the popular reselect package:
Selectors can compute derived data, allowing Redux to store the minimal possible state.
Selectors are efficient. A selector is not recomputed unless one of its arguments change.
Selectors are composable. They can be used as input to other selectors.
Why doesn't actions.ERROR_IMAGE_EXISTS(url) work for me
Because I just made that up for the example. The point is that you can dispatch actions from within the thunk, how you declare or get access to the action is up to you. I tend to centralise all my shared actions into an actions object that I import.
Selector usage example
Here is an example from my real-life code that shows how I use selectors to passing portions of the state as props to a react component:
const mapStateToProps = (state) => ({
model: services.model.selector.getSelected(state),
build: services.build.selector.getLastBuild(state),
recommendations: services.recommend.selector.getRecommendations(state)
})
Each of these selectors is finding the correct portion of the state tree and delivering it back ready for use. Nice and tidy, and if you use reselector, very efficient.

Redux Pattern for Comparing a Cached State?

Consider the following Redux code,
let cacheState = {};
const mapStateToProps = (state, ownProps) => {
const zug = cacheState.bar === state.bar ? "qux" : "doo";
return {
foo: state.bar,
zug
};
cacheState = state;
};
This code example spurs the following observations:
1) This seems to be the only way to cache state in a Redux component
2) This will break if multiple Redux components are created from this template
3) This is an anti pattern
What is the correct way for a component to be aware of previous states?
You likely want to use middleware to accomplish this. When your store is created, you'd do the first comparison (say if you were using localStorage) and store the cached state. You would then assume both your store and cache are synced. Every action that follows would go through the middleware and sync the state back with the cache.
You're right that what you're doing here is an anti-pattern which is why the middleware approach is useful in avoiding it. Dan Abramov's second set of Redux videos goes over this exact pattern if you'd like to check em out by searching the Building React Applications with Idiomatic Redux in Egghead, or here.

Resources