Using vuex with Vue 3 - firebase

Last year I spent some time learning Vue 2. I really enjoyed the framework but did not move forward with a project. I now have time for a project but I'd like to use Vue 3 with the composition API. I'll be using Firebase as the backend. I've seen conflicting techniques on whether or not to use Vuex.
For example, I'd like to store a "currentUser" object in global state that can be accessed from any component in the app. Normally, it would be done using Vuex. I'd have a getter to return the object, an async action to get the data from firebase and a mutation to set the state.
However, I've seen several Vue 3 code examples that do not use Vuex at all, instead they do something like this to get/set a currentUser in an app wherever it is needed for example in a navbar component.
composables/getUser.js
import { ref } from 'vue'
import firebase from 'firebase/app'
// refs
const user = ref(firebase.auth().currentUser)
// auth changes
firebase.auth().onAuthStateChanged(_user => {
console.log('User state change. Current user is:', _user)
user.value = _user
});
const getUser = () => {
return { user }
}
export default getUser
With this little bit of code above, I'm able to import getUser.js and access the currently logged in user using code like this. Also, the user object is now reactive:
<script>
import getUser from '../composables/getUser'
export default {
setup() {
const { user } = getUser()
return { user }
}
}
</script>
It seems I can use these little functions to get data from db directly without the need to use the Vuex pattern, which I find to be a bit more complicated.
So, my question is - if I'm starting a new Vue 3 project, is it ok to create "composable" functions to get/set data and import them into my components instead of using Vuex? Are there any downsides to this method? Or should I just stick with Vuex?

Short answer - You don't need it.
Long answer - It depends.
It depends mostly on your app and how often do you use "currentUser" data inside your components. If it's in 2 or more components, how often do you want to perform actually fetching from backend?
Once on app-init/login or every time each component mounts?
(probably once)
Does it need to be reactive? If yes - then you'll probably use centralized data pattern, your own or a library. Taken that into consideration it's probably more simple to just use Vuex.

Related

Exclude redux-form reducer from main store

import { combineReducers } from 'redux'
import { reducer as form } from 'redux-form'
combineReducers({
router: connectRouter(history),
form,
....
// huge amount of other reducers
})
Above you can see combined reducers in one store. As bigger it becomes as slower it becomes because on each action it should make many more checks as it was at the beginning. As you also can see I use redux-form for handling state in my form. And it starts to be slower and slower when I type in redux-form fields. I want to find out is there any way to create some individual store for redux-form, and prevent this from slowing down. Or is there exist some other solutions to this problem?
Using redux you can have multiple stores.
redux-form is using connect API from react-redux to get info from store.
With React Redux, the wrapper classes generated by the connect() function do actually look for props.store if it exists, but it's best if you wrap your root component in and let React Redux worry about passing the store down. This way components don't need to worry about importing a store module, and isolating a Redux app or enabling server rendering is much easier to do later.
https://redux.js.org/faq/store-setup#can-or-should-i-create-multiple-stores-can-i-import-my-store-directly-and-use-it-in-components-myself
I'm a redux-form collaborator.

How can I ensure my not yet fetched data doesn't break my site

I am currently building a like button on my card component in vue. I am fetching data from firebase using middleware on a page to dispatch the vuex action that will go and get my user info which has their liked_posts stored in an array.
The issue comes up that when I load a page requiring some of the data
i.e. liked_posts and my state is empty it throws a error of
"undefined".
How can I make sure that even if the user hasn't signed in or hasn't ever visited that my user data wont cause an error
I have tried to change my action in the Vuex store to be asynchronous and use await so that I made sure the data was there, but it didn't help.
What is happening is the below code in computed properties is trying to access an object that doesn't exist in the array yet.
likedOrNot() {
const likeInfo = this.$store.state.userInfoSub[0].liked_posts
return likeInfo.includes(this.$store.state.loadedCards[0].id)
}
This data isn't there yet because the user isn't signed in, exist ect. once they do and middleware is dispatching an action to fetch the user data the userInfoSub will be filled with info.
my base state looks like this when the user hasn't signed in or middleware hasnt fired to look for the user that gets put in cookies.
So I need away to ensure my lack of userInfoSub doesn't break my computer property
loadedCards:Array[1]
0:Object
token:null
user:null
userInfoSub:Array[0]
username:null
Here's an opinionated answer: use get from lodash.
npm i lodash
Then you can write something like this:
import get from 'lodash/get';
export default {
computed: {
isLiked() {
const cardId = get(this.$store, 'state.loadedCards[0].id');
const postIds = get(this.$store, 'state.userInfoSub[0].liked_posts', []);
return postIds.includes(cardId);
},
},
};

react-redux connect should have side effects?

I have a component which need logged-in user data but in this component I'm not sure if user is logged in yet or not
is it a bad practice to check existance data in mapStateToProps and redirect to login page if needed? is it a side effect?
what about fetching data which is nit exist in state yet?
how should I handle such problems?
what should we do in mapState and what we shouldn't do?
is there any good practice for it?
it a bad practice to check existance data in mapStateToProps and redirect to login page if needed?
I am not sure of whether it's a best practice or not, but if we look at the semantics of mapStateToProps, it's main usage is mapping redux state to component props.
So in that sense anything that is not mapping can be considered a side-effect.
If you want to align with redux-way then you pass down with mapStateToProps properties that you want to react to, and you change those properties with mapDispatchToProps.
So in your case you would have some sort of Authentication service, that would have methods like login, logout, register, which would dispatch actions to your redux store, and in mapStateToProps you would subscribe to something like isAuthenticated.
And in your render you would check if this.props.isAuthenticated ? <PrintSome> : null
You can also check this tutorial with basically same idea, but taken out into a higher order component for reusability https://medium.com/quick-code/adding-authentication-to-react-redux-firebase-app-f0efcb1c519a
So let's assume you have defined your routes in the src/components/App.js component. You are at a point where you need to ensure that your App component knows whether or not the user is actually signed in at all.
Let's also assume that you have already developed a reducer in reducers/auth.js to record whether a user is signed in or not and you have assigned the authReducer to your auth piece of state.
At this point, inside your src/components/App.js file you would need to import:
import { connect } from 'react-redux';
Then at the bottom of your App component you need to define your mapStateToProps like so:
function mapStateToProps(state) {
return { auth: state.auth }
}
export default App;
So what I am recommending above is that you pass state to mapStateToProps and then return an object with a key of auth and that will take whatever value is coming out of the authReducer.
Then you take the connect() function you imported and pass mapStateToProps to it like so:
export default connect(mapStateToProps)(App);
So now your App component knows whether or not the user is signed in.
So what is an advantage of implementing the above? Well, it gives you the flexibility of determining what to display in your view depending on whether the user is signed in or not. For example, a sign-in/sign-out button.
You can now develop that button with a helper method that has a conditional like so:
renderButton() {
if (this.props.auth) {
return (
<button>
Sign Out
</button>
);
} else {
return (
<button>
Sign In
</button>
);
}
}

Local storage with Redux

I have recently begun coding in Redux.
Before Redux with AngularJS it was easy to map models with state using $localstorage. I just can figure out the best way to do that with Redux.
Should I be dispatching and action and ask reducers to read local storage for in my code ?
Or should I allow local storage to be managed with a global object ?
There are few ways.
Just note that for syncing to localStorage you need to call JSON.stringify which is quite expensive, so please don't do that often and also with large data structures as it might hurt app's performance.
1) Sync whole Redux store to Local Storage. You can use existing solution for that eg. https://github.com/elgerlambert/redux-localstorage
I would not recommend to sync whole store as you might sync also state which should not be persisted after refresh and also you might make application slower; For better performance you can use paths argument in above library or use one of another options.
To see how you can build such functionality manually, there is great explanation video from Dan https://egghead.io/lessons/javascript-redux-persisting-the-state-to-the-local-storage
2) Manually build simple cache middleware like below, which might catch specific actions you would like to sync with local storage
const cacheMiddleware = store => next => action => {
if(action.type !== 'GET_SOMETHING') {
return next(action);
}
const data = getFromLocalstorage();
if(!data) {
// Fetch and put to localstorate for later use
const data = fetchFromServer();
return next({ type: 'SERVER_RESULT', data });
}
return next({ type: 'CACHED_RESULT', data });
};
3) If you are using Redux Thunk you can perform caching there as you are allowed to have side effects in actions.
You can find more info about Redux middleware here https://redux.js.org/advanced/middleware

How to have multiple reducers trigger updates based on a common set of actions without repeating yourself?

I would like many different redux actions in my app to all trigger common functionality in a specific reducer. I would like to avoid having to either repeat some flag in every action creator (like doThing: true) that the reducer looks for. I also don't want to have to have the reducer just look for every individual action that falls into this category, since that also requires someone to remember to do this every time they add a new action, like adding the flag.
I was thinking of dispatching a second action every time one of these actions is going to be dispatched. This would not be hard to do, but I'd rather not have 2 actions dispatched every time one thing happens. It seems like it would pollute the state history.
Is there a common way of solving this problem?
For more context to my specific problem, the specific feature is related to the API client my app uses to talk to our API. On every successful response, we'd like to do something in a reducer to update the state, and on every failed response, we'd like to do something else.
There are many different success and failure actions (such as ITEM_FETCH_SUCCESS or WIDGET_UPDATE_FAILURE), and adding a flag to all of them would be hard to remember to do when new ones are added.
Since all api requests go through a single function, that function COULD dispatch generic REQUEST_SUCCESS and REQUEST_FAILURE actions. But this would mean every response from the server would dispatch 2 actions (REQUEST_SUCCESS and ITEM_FETCH_SUCCESS). This is obviously not ideal since it would mean many more actions in my state history.
Assuming the generic REQUEST_SUCCESS and REQUEST_FAILURE actions are updating their own specific portions of the state-tree then it is fine to dispatch them as distinct actions. Doing this does not necessarily imply the pollution of your state history but can simply be a better description of the app's intentions.
ITEM_FETCH_SUCCESS: Change state for item
REQUEST_SUCCESS: Change state for request
WIDGET_UPDATE_FAILURE: Change state for widget
REQUEST_FAILURE: Change state for request
You can see that whilst the actions are intimately related, they are not necessarily the same thing as they change different parts of the state tree.
Accepting this, the question is: How best to implement the action-pairs so that adding new actions does not mean remembering to add its corresponding REQUEST_* partner?
I would consider applying a simple redux middleware component. This could intercept the return from your api and dispatch the appropriate REQUEST_* action automatically.
Here is an example from some live code. This middleware intercepts a disconnect event raised by a websocket and automatically dispatches a custom action as a result. It at least shows the principle:
//Dispatch a disconnect action when the websocket disconnects
//This is the custom action provided by the middleware
import io from 'socket.io-client'
import { actions } from './action'
const websocket = ({ websocketUrl }) => store => {
const socket = io(websocketUrl)
socket.on('disconnect', () => store.dispatch(actions.disconnect()))
}
export default websocket
//Apply the custom middleware via the redux createStore function
//Also include the thunk middleware because it is useful
import { applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import websocket from './middleware'
function websocketize (opts) {
return createStore => (reducers, initial, enhancer) => {
const middleware = applyMiddleware(thunk, websocket(opts))
return createStore(reducers, initial, middleware)
}
}
export default websocketize
// Create the top-level redux store passing in the custom middleware enhancer
const opts = {websocketUrl: env.WEBSOCKET_URL}
const store = createStore(reducers, websocketize(opts))
This implementation keeps everything inside your reducers as opposed to having logic outside in an interception(middleware). Both ways are valid.
Try a sub-reducer pattern. I usually feel gross when I see it used(because it is usually used wrong), but your situation sounds perfect.
Extract duplicate functionality out of your reducers to one single
sub-reducer.
Then pass that reducer as a function to all others that need it.
Then pass the action and state onto the sub-reducer.
The sub-reducer does it's thing and returns that slice of state to
your parent reducer to allow you to do whatever you want with it
there (ie return it, mutate some more, some logic).
Also if you are tired of worrying about typing out "all the stuff" for async then I highly recommend you try out redux-crud.js
It also is possible and a simple way to do that would be to give every action to one reducer and let it do that common mutation, in a single case:
case actionOne
actionTwo
actionThree
actionFour: {
//do common stuff here
}
. But you said it is not duplicated, it is similar, which means your case becomes complicated by branching logic. I also don't recommend this. Keep cases simple so you can easily catch invalid mutations. This should be a super power of redux that it is easy to catch mutation errors. And for this reason and many others I don't recommend normalizing data in the front end.

Resources