Devtools for React Easy State? - reactjs

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.

Related

Losing a part of my state in Redux Toolkit

I am trying to store the cart state for an ecommerce project. This is my first time using Redux Toolkit. In the first couple of images you can see the setup for the states and what the localstorage looks like when starting.
Application part of Chrome after clearing cookies
cartSlicer for Redux Toolkit
store.js
storage.js
Then, after using the application for a bit, I seem to lose the cart_total, cart_quantity and active states, leaving only the "mother" cart state that encapsulates all the states.
As you can see, I no longer have the objects from the initialState, only "cart"
This causes the application to break, since there is no longer a cart.cart level in the localstorage, like in the first image from the dev tools, which causes me to get undefined-errors.
I'm sure this is because I'm probably making a stupid mistake in how I'm setting things up. Any help would be greatly appreciated! If anything was unclear, please let me know!
Best regards, Ragnsan
Update!
I seem to have solved the issue. I figured out that the state was lost every time I tried to use the removeFromCart dispatcher. I changed it to:
removeFromCart(state, action) {
const index = state.cart_items.findIndex((item) => item.id === action.payload);
state.cart_items.splice(index, 1);
},
Can someone explain why this worked as opposed to:
return state.cart_items.filter((item) => item.id !== action.payload.id);
Either way, it worked!

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

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)

Do I have to save every React component state property to the Redux store?

I have read multiple articles on the need to use Redux and have built two fully-functioning React+Redux applications. I have even posted the question on Quora I still cannot have a final answer to my question:
Do I have to save every component state property to the Redux store?
The first project, I have built by following a tutorial where he basically saves everything to the store.
Here's the Github link.
Since I was learning React and Redux, I did not question this approach and went on with it. But, it does seem somewhat unnecessary to save everything to the store
For example, there's an action that saves the comment data to the store:
postActions.js
// Add Comment
export const addComment = (postId, commentData) => dispatch => {
dispatch(clearErrors());
axios
.post(`/api/posts/comment/${postId}`, commentData)
.then(res =>
dispatch({
type: GET_POST,
payload: res.data
})
)
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
And it is called like this:
CommentForm.js
onSubmit(e) {
e.preventDefault();
const { user } = this.props.auth;
const { postId } = this.props;
const newComment = {
text: this.state.text,
name: user.name,
avatar: user.avatar
};
this.props.addComment(postId, newComment);
this.setState({ text: '' });
}
If I were working on my own project, I would've kept the message data stored locally at the component level:
The second project was a personal project, where the only data I saved in the store, is the user account information because I would need it in different components throughout the app to send it in some backend API requests.
All the other components are basically independent or the flow between them does not go beyond two or three components. So I really could not see why I would make myself code all the actions, reducers...etc for all of the components. So I simply pass the props and functions between components in the plain old react way of doing things.
Most of the answers that I found do not go into this specific detail mentioned in my question. All of them talk from a high-level perspective.
Before going ahead and working on other projects, I would like to :
A clear answer to my question
Whether the approach I used for my personal project is okay. In other words, can I use Redux simply for the user account information and for the rest of the components not use it?
I just want to clear this confusion so that when I am using Redux, I am 100% sure, I am using it because I actually need it.
Do I have to save every component state property to the Redux store?
Short answer: No you don't.
Longer answer: To quote Dan Abramov on a similar question:
Use React for ephemeral state that doesn't matter to the app globally and doesn't mutate in complex ways. For example, a toggle in some UI element, a form input state.
Use Redux for state that matters globally or is mutated in complex ways. For example, cached users, or a post draft.
There is nothing wrong with the approach taken in your personal project. Redux is great for storing/sharing global application state, such as the user info you describe.
Before putting state into Redux I'd ask:
Will this state be consumed by other components independent to this one?
If the answer to #1 is yes: then ask how often?
If the answer to #2 is frequently: then ask is a single source of truth (the Redux store) the best way to share this particular piece of state? Would
other techniques (hooks / render props / higher order components) be more appropriate?
Another quote from Dan in the same linked thread is:
If it gets tedious and frustrating don’t be afraid to put state into the components. My point is that use single state tree unless it is awkward, and only do this when it simplifies things for you rather than complicates them. That’s the only guideline.
The mantra Yagni (You Aren't Gonna Need It) springs to mind.
If you're unsure whether state should be abstracted from a component into Redux, then the chances are it's too early todo so. This helps avoid making design decisions too early, whilst keeping your Redux state lean and intentional (i.e: not convoluted with unnecessary single use concerns).
Ultimately the cost of putting state into Redux needs to pay off.

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

Global variables in React

I know Redux solves this but I came up with an idea.
Imagine I have an app that gets some JSON on start. Based on this JSON I'm setting up the environment, so let's assume the app starts and it downloads an array of list items.
Of course as I'm not using Redux (the app itself is quite simple and Redux feels like a huge overkill here) if I want to use these list items outside of my component I have to pass them down as props and then pass them as props again as deep as I want to use them.
Why can't I do something like this:
fetch(listItems)
.then(response => response.json())
.then(json => {
window.consts = json.list;
This way I can access my list anywhere in my app and even outside of React. Is it considered an anti-pattern? Of course the list items WON'T be changed EVER, so there is no interaction or change of state.
What I usually do when I have some static (but requested via API) data is a little service that acts kind like a global but is under a regular import:
// get-timezones.js
import { get } from '../services/request'
let fetching = false
let timez = null
export default () => {
// if we already got timezones, return it
if (timez) {
return new Promise((resolve) => resolve(timez))
}
// if we already fired a request, return its promise
if (fetching) {
return fetching
}
// first run, return request promise
// and populate timezones for caching
fetching = get('timezones').then((data) => {
timez = data
return timez
})
return fetching
}
And then in the view react component:
// some-view.js
getTimezones().then((timezones) => {
this.setState({ timezones })
})
This works in a way it will always return a promise but the first time it is called it will do the request to the API and get the data. Subsequent requests will use a cached variable (kinda like a global).
Your approach may have a few issues:
If react renders before this window.consts is populated you won't
be able to access it, react won't know it should re-render.
You seem to be doing this request even when the data won't be used.
The only downside of my approach is setting state asynchronously, it may lead to errors if the component is not mounted anymore.
From the React point of view:
You can pass the list from top level via Context and you can see docs here.
Sample of using it is simple and exists in many libraries, such as Material UI components using it to inject theme across all components.
From engineering concept of everything is a trade of:
If you feel that it's gonna take so much time, and you are not going to change it ever, so keep it simple, set it to window and document it. (For your self to not forget it and letting other people know why you did this.)
If you're absolutely certain they won't ever change, I think it's quite ok to store them in a global, especially if you need to access the data outside of React. You may want to use a different name, maybe something like "appNameConfig"..
Otherwise, React has a feature called Context, which can also be used for "deep provision" - Reference

Resources