I am using redux persist to automatically persist and rehydrate the state on application launch as described in the docs, specifically using AsyncStorage: https://github.com/rt2zz/redux-persist.
It works correctly for String. That is all state which is store as a String is persisted and rehydrated correctly.
However for custom object maps it does rehydrate the state correctly. I am pretty sure this is because Strings are serializable and object maps aren't.
This is the reducer where productTypeToProducts is represents that map. When Re- hydrated is it empty
case 'RECEIVE_ALL_PRODUCTS':
return Object.assign({}, state, {
productTypeToProducts: action.productTypeToProducts
});
Hence I need to use something like JSON.stringify to covert my state tree into JSON.
How do I implement this so that on application launch the the object state (not the JSON) is automatically rehydrated. (I am guessing this has something to with using transforms in the docs ?)
It seems like by default AsyncStorage used in conjuction with redux-persist, does properly serialize an array of object. So I converted the Map to Array below in my reducer and it properly hydrated the state.
case 'RECEIVE_ALL_PRODUCTS':
let productsObject = {};
action.productTypeToProducts.forEach((value,key)=>{
productsObject[key] = value
});
return Object.assign({}, state, {
productsAsObject: productsObject,
});
Related
I have large amount of data that I'm storing in local storage using react persist. What I need is to know when persist data were restored in my reducer (when app is loading). I need to validate data version and I need to generate lookup object (which redux-storage us unable to store, probably because because it has around 65405 records/fields).
Anyway I would like to know when react-persist is loading my data so I can work with them. How do I achieve this?
There are two special actions in redux-persis: PERSIST and REHYDRATE.
The PERSIST action has been dispatched and then with REHYDRATE, the saved object of the store will be load and injected into the current redux store. So, you can import them into your reducer functions and be aware of persisting status.
You need to manipulate the data before re-hydrated them with persistor, but I recommend doing a simpler approach to handle this case.
For example, there is a data value (which is contain 65405 records):
case DATA_ARRIVED_SUCCESSFULLY:
return {
...state,
data: actin.payload
}
Now, before persisting and before putting the payload on data, you can check and validate your data:
const sampleValidatorData = (myData) => {
// do something with myData
}
case DATA_ARRIVED_SUCCESSFULLY:
return {
...state,
data: sampleValidatorData(actin.payload)
}
Now, your saved data (with persistor) will be valid.
I am building an application and am trying to show article content from mongoDB using axios, node.js, and express on a React frontend. I can pull the data to my redux store but when I try to display it I get a cannot read property 'author' (or whatever property in the state I'm going for) of undefined error.
Here are some screenshots of my react file, as well as the action and reducer. Let me know what other info you might need. Thanks a ton.Here is my React file. Here is the action. here is my reducer.
Because the initial of state is empty object {}. So while fetching API, state.articles is undefined and this issue occur.
You can fix by add condition before using with optional chaining
const articles = useSelector((state => state.articles);
const current = articles?.current ;
In my react + redux app, I want to dynamically display some notifications/message about new features/updates as header on our react + redux app. This message to be displayed would be entered as input type text in one of the component (Alert.js) . I capture the entered value as props in this component and pass this props to a child component(Header.js) which just displays the message captured. Then i simply call the child component Header in App.js passing the required props.. The issue i am facing is when the app reloads the component re-renders, the props value becomes undefined and the message that i want to display in header goes away. How do retain a object's value in store until the entered input value is not changed. I am new to react and redux. Please help.
Thanks
You can set up an initial state for your reducer like so
const initialState = {
message: "Welcome back"
}
const alertReducer = (state = initialState, action) => {
switch(action.type){
case "NEW_ACTION":
return action.payload
default:
state
}
}
This will give your app predefined data to be used in your components. To have dynamic data that you can preserve though, you're going to have to integrate a database like MongoDB or Firebase. That way you can pull data like "most recent alerts" and etc. Otherwise, there is no way for you to effectively persist data solely on React and Redux. Maybe local storage, but that data will only exist on your computer.
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
From redux doc ([http://redux.js.org/docs/api/Store.html][1]):
A store is not a class. It's just an object with a few methods on it.
and the methods are:
getState()
dispatch(action)
subscribe(listener)
replaceReducer(nextReducer)
(In flux it is is similar, with a difference that there is also an ActionDispatcher (and possibly an EventEmitter), but Store(s) are registered to the ActionDispatcher, so they are coupled.)
The question is why? Why store is not just a decoupled data type without logic, preferably an immutable one.
The following pseudo code is an example to show what I am trying to say. I used "appState" word instead of "store", as it is more natural to me:
const initialAppState = require("./initial-app-state.json");
function main() {
var actionDispatcher = new ActionDispatcher();
var appState;
actionDispatcher.register(function onAction(action) {
var newAppState = appState = reducers_combined(appState: ?AppState, action); //apply the action to appState, and create a new app state; as state is immutable
var newAppProps = createAppProps(newAppState); //we can write the createAppProps function, which takes an app state and create all the props to be passed down to the root component
ReactDom.render(React.createElement(App, newAppProps), document.getElementById("root"));
});
actionDispatcher.dispatch({
type: "LOAD_APP_REQUESTED",
appState: recordify(initialAppState); //we can write the recordify function that turns initialAppState JSONValue to an Immutable Record
})
}
As above we can create our ActionDispatcher single instance in our main function, and register an onAction callback to it, which has access to the current app state via closure, and will in turn create the new app state, update the current app state reference to the newly created one, create new app props with respect to the new app state and render it. The views will dispatch actions to the actionDispatcher directly (the actionDispatcher instance can be passed down the component tree via context) or indirectly.
What is the rationale making the Store work, instead of just hold? Are there any advantages?
Afaik because in redux when creating a store a reducer is required.
The store is usually created with createStore(reducer, [preloadedState], [enhancer]).
The functions mentioned in your question are utility function useful when working with the store.