I want to increment the count of the value each time when I click on the new widget.
Whenever I click my new button I need to get incremented by +1 (e.g. Chart1, Chart2, Chart3)
As of now my values are stored in the state, so when I click add it gets incremented (Chart1, Chart2) but if I refresh the page again it starts with Chart1.
Here is my code
this.state = {
widgetCount: 1,
widgetName: '',
}
addWidget = (widgetType) => {
this.showChartTypesModal.hide();
const newWidget = this.getNewWidget(widgetType);
this.setState(
{
widgetsList: [...this.state.widgetsList, newWidget],
isHidden: !this.state.isHidden,
widgetCount: this.state.widgetCount + 1,
},
() => this.props.storyboardActions.addWidget(newWidget,
this.props.selectedBoard.boardId, updatedSwimlanes)
);
};
case 'bar':
return {
widgetName: this.state.widgetName || `Chart ${this.state.widgetCount}`,
};
case 'pie':
return {
widgetName: this.state.widgetName || `Chart ${this.state.widgetCount}`,
};
Should I use redux to maintain the state? Or is it possible to do without that.
Please help me with this
State is transient as HTTP is stateless. You need to save it in browser or in external database.
I am assuming that by "refresh" you are refreshing the browser.
Redux won't help you at all in this case.
If you want to persist the state across browser refresh, here are some options.
Use localStorage - stored in a browser
Use indexedDB - stored in a browser
Persist state/data in external database - firebase/postgres/mysql, etc.
Benefit of first two options is that, it's easy to implement, but it won't survive when a user refreshes the browser cache.
3rd option is the hardest but it will persist the state regardless of browser setting.
As has been said, State in redux does not persist. However, there is a library that will address this issue if you need persistence: redux-persist. This would mean that the state does not reset after each refresh.
Related
I'm having some issue with my context file. I have a file called "auth-state" that I use to manage application wide authentication state. It initializes a react context object, and has several helper functions defined that help manage things like logging out, verifying user authentication tokens, and logging in. Excluding a lot of the helper functions, it looks like this:
const noUserObj = {
userName: "Anonymous",
isLoggedIn: false,
authToken: "-",
projList: null,
attemptLogIn: () => {},
userLogout: () => {}
}
const AuthState = React.createContext(noUserObj);
const AuthStateProvider = (props) => {
const authCtx = useContext(AuthState);
let existingToken;
if(localStorage.getItem("token")) {
existingToken = localStorage.getItem("token");
} else {
existingToken = "-"
}
const [ user, setUser ] = useState(noUserObj);
return (
<AuthState.Provider
value={{
userName: user.userName,
isLoggedIn: user.isLoggedIn,
authToken: user.authToken,
projList: user.projList,
attemptLogin: attemptLoginWithAxios,
userLogout: logoutWithAxios
}}
>
{props.children}
</AuthState.Provider>
)
}
export {AuthStateProvider};
export default AuthState
I use a state variable to manage the actual context values and then pass the state variable to the context providers "value" prop. I know you can't see it here, but whenever one of the helper functions log in or out, they update the "user" state variable, and this in turn updates the context values. From my understanding this is relatively standard practice. My issue is that when trying to access the context values from within the auth-state context file itself, I always get outdated or "default" states.
This is especially confusing because when I use the react-dev tools in the chrome browser to look at the values currently saved in the auth-provider, everything looks right. I can see the values update as I login/out, and watch it function as intended. But whenever I try to access the context values from within auth-state (whether that be by using useContext(authState) or by accessing the "user" state variable itself) I only ever get the default object you see declared at the top of the authState file (noUserObj).
This is an issue because one of those helper functions I mentioned earlier (one that logs users out) requires the users authtoken to be able to log them out. And I defined this helper function inside the auth-state file explicitly because it would have easy access to all auth related variables it needed. But everytime I try to access the state variable or the context itself I only get default. I'll even run it in debug mode, pause the execution right before "logging out" runs, and see that the context variable is properly updated, but the helper function only gets default values.
I don't know what the heck is happening, but right now I'm just working around it by passing the the helper functions tokens from outside the file, wherever I actually call them. (this is even weirder because I get these tokens FROM THE AUTH-CONTEXT, its just for some reason when I use useContext(auth-state) outside of the auth-state file itself, everything works fine.) Whatever the case, I'm sure I'm just overlooking something small. Thank you in advance for the help.
I'm trying to change firebase language dynamically, meaning that I have a button on the page that allows me to switch language which triggers the handling below:
useEffect(() => {
if (!firebase) return;
// This sets up firebaseui
let localizedFirebaseui;
switch (langCode) {
case 'hu':
localizedFirebaseui = require('../intl/firebaseui/npm__hu.js');
break;
case 'pl':
localizedFirebaseui = require('../intl/firebaseui/npm__pl.js');
break;
default:
localizedFirebaseui = require('../intl/firebaseui/npm__en.js');
break;
}
// Configure FirebaseUI.
const uiConfig = {
// Popup sign-in flow rather than redirect flow.
signInFlow: 'popup',
// We will display Google and Facebook as auth providers.
signInOptions: [
originalFirebase.auth.EmailAuthProvider.PROVIDER_ID,
originalFirebase.auth.GoogleAuthProvider.PROVIDER_ID,
originalFirebase.auth.FacebookAuthProvider.PROVIDER_ID,
],
signInSuccessUrl: ROUTE_PATH_TYPEWRITER,
tosUrl: ROUTE_PATH_TERMS_OF_USE,
privacyPolicyUrl: ROUTE_PATH_PRIVACY_POLICY,
};
const authUi = new localizedFirebaseui.auth.AuthUI(originalFirebase.auth());
firebaseuiRef.current && authUi.start(firebaseuiRef.current, uiConfig);
return () => {
authUi && authUi.delete();
};
}, [firebase]);
firebase is an initialized instance in this case, basically on every render of the page useEffect is called which allows me to load localized data and start the UI, and then I delete the instance to be able to reinitialize it again with a different set of translations.
The problem is that the language switch works until I'm choosing every language for the first time, for instance, if I start with en, switch to pl, I'm not able to go back to en, but loading hu will work properly.
Language change itself is fine because the rest of the app is switching without an issue.
Any ideas on how to fix it? Or maybe it's not even possible, or there is a better solution than deleting and starting the UI over and over? I'm having a hard time finding anything related to the topic, I don't know why, is it some trivial error that no one faced before?
I'm facing an issue with realm.js. I add an object and give permission to that, and it seems to work well, but when I log out and then log in with another account I can see the previous data on device (even the ones for those I don't have permissions, that I can only read but not edit).
So now, I don't have permissions to read or query those data but I'm able to receive live updates (not as expected) and see them! How can I prevent that to happens?
Thank you
I think you need to clear your redux store as defined below.
const { Types, Creators } = createActions({
desktopSuccess: ['payload'],
desktopClear: [],
});
export const desktopReducer = createReducer(INITIAL_STATE, {
[Types.DESKTOP_SUCCESS]: success,
[Types.DESKTOP_CLEAR]: clear,
});
On click of logout clear your store as shown under:
onPressLogOut = () => {
AsyncStorage.clear();
store.dispatch(DesktopAction.desktopClear());
this.props.navigation.navigate('LoginScreen');
};
Okay. I'm kinda new to react and I'm having a #1 mayor issue. Can't really find any solution out there.
I've built an app that renders a list of objects. The list comes from my mock API for now. The list of objects is stored inside a store. The store action to fetch the objects is done by the components.
My issue is when showing these objects. When a user clicks show, it renders a page with details on the object. Store-wise this means firing a getSpecific function that retrieves the object, from the store, based on an ID.
This is all fine, the store still has the objects. Until I reload the page. That is when the store gets wiped, a new instance is created (this is my guess). The store is now empty, and getting that specific object is now impossible (in my current implementation).
So, I read somewhere that this is by design. Is the solutions to:
Save the store in local storage, to keep the data?
Make the API call again and get all the objects once again?
And in case 2, when/where is this supposed to happen?
How should a store make sure it always has the expected data?
Any hints?
Some if the implementation:
//List.js
componentDidMount() {
//The fetch offers function will trigger a change event
//which will trigger the listener in componentWillMount
OfferActions.fetchOffers();
}
componentWillMount() {
//Listen for changes in the store
offerStore.addChangeListener(this.retriveOffers);
}
retrieveOffers() {
this.setState({
offers: offerStore.getAll()
});
}
.
//OfferActions.js
fetchOffers(){
let url = 'http://localhost:3001/offers';
axios.get(url).then(function (data) {
dispatch({
actionType: OfferConstants.RECIVE_OFFERS,
payload: data.data
});
});
}
.
//OfferStore.js
var _offers = [];
receiveOffers(payload) {
_offers = payload || [];
this.emitChange();
}
handleActions(action) {
switch (action.actionType) {
case OfferConstants.RECIVE_OFFERS:
{
this.receiveOffers(action.payload);
}
}
}
getAll() {
return _offers;
}
getOffer(requested_id) {
var result = this.getAll().filter(function (offer) {
return offer.id == requested_id;
});
}
.
//Show.js
componentWillMount() {
this.state = {
offer: offerStore.getOffer(this.props.params.id)
};
}
That is correct, redux stores, like any other javascript objects, do not survive a refresh. During a refresh you are resetting the memory of the browser window.
Both of your approaches would work, however I would suggest the following:
Save to local storage only information that is semi persistent such as authentication token, user first name/last name, ui settings, etc.
During app start (or component load), load any auxiliary information such as sales figures, message feeds, and offers. This information generally changes quickly and it makes little sense to cache it in local storage.
For 1. you can utilize the redux-persist middleware. It let's you save to and retrieve from your browser's local storage during app start. (This is just one of many ways to accomplish this).
For 2. your approach makes sense. Load the required data on componentWillMount asynchronously.
Furthermore, regarding being "up-to-date" with data: this entirely depends on your application needs. A few ideas to help you get started exploring your problem domain:
With each request to get offers, also send or save a time stamp. Have the application decide when a time stamp is "too old" and request again.
Implement real time communication, for example socket.io which pushes the data to the client instead of the client requesting it.
Request the data at an interval suitable to your application. You could pass along the last time you requested the information and the server could decide if there is new data available or return an empty response in which case you display the existing data.
I am trying to create a status component in my application, which will be updated during data fetch and other network related events.
componentWillReceiveProps(nextProps){
// This check is required otherwise setting state
// from here will end up in a rendering loop
if(!_.isEqual(this.props, nextProps)){
if(nextProps.services.length != 0){
if(nextProps.filters.length == 0){
nextProps.setStatus('Fetching filters...');
const { serviceId } = nextProps.params;
const serviceIndex = nextProps
.services
.findIndex(
(service) => service.id == serviceId
);
const serviceObj = nextProps.services[serviceIndex];
nextProps.getFilters(serviceObj);
}
}
}
}
Here I am using componentWillReceiveProps life cycle event to load some data based on another set of data in store (due to dependency). The setStatus method is responsible for updating the store with the new status message and according to that a Status component will be updated. Here you can see, I am checking if the filters are empty, I am setting the status and trying to fetch the data. Now when I am setting the status it is updating the store thus firing the "render" process again. This will call the life cycle events again and as the filters are still empty it will do the data fetch request again. Currently, I have stopped this using
// Setting status will set the state and invoke data collection
// again as filters are still empty due to re-render
if(_.isEqual(this.props.status,nextProps.status) &&
_.isEqual(this.props.filters,nextProps.filters)) {
const { serviceId } = nextProps.params;
const serviceIndex = nextProps
.services
.findIndex(
(service) => service.id == serviceId
);
const serviceObj = nextProps.services[serviceIndex];
nextProps.getFilters(serviceObj);
}
But this approach is more like a hack. Can you please suggest any better design approach for this status component? Can I maintain a controlled component?
I'll admit that what you are specifically trying to accomplish is a bit unclear to me, but from what it seems, I should think you should be creating a Redux event to inform your component of the different steps of this process and hitting action creators in response. If you're trying to track state in some way outside of the Redux store, you'll get yourself into trouble.