How can I connect to the store in non-react components - javascript

I would like to connect to the store in non-react components. My main problem is that the when i try to getState or dispatch in a non-react class like this: createStoreWithApi(api).dispatch(isLoading(true)) this will create a new instance of the store, and I just want to modify the already created store. And I know that I would have to avoid having the store as a function.
Is it possible to set the withExtraArgumentafter the store is created? Problem is that I cant just import the api in my store file, because I need to fetch the api from the backend first.
This is how my store set-up looks like:
const createStoreWithApi = (api: IApi, initialState?: {}) => {
const middlewares = [
thunkMiddleware.withExtraArgument({
api
})
];
const enhancer = composeWithDevTools(applyMiddleware(...middlewares));
return createStore(rootReducer, initialState!, enhancer);
};
Would love some advice

You can create a single instance of store in a create-store.js file that holds onto the store singleton. Then import that instance into all files that need access to it for store.getState and store.dispatch.
// create-store.js
let store = null;
export const createStoreWithApi = (api: IApi, initialState?: {}) => {
const middlewares = [
thunkMiddleware.withExtraArgument({
api
})
];
const enhancer = composeWithDevTools(applyMiddleware(...middlewares));
store = createStore(rootReducer, initialState!, enhancer);
};
export default store;
In your entrypoint file, you can run createStoreWithApi once with api and initialState.
// index.js
import { createStoreWithApi } from "./create-store";
// init store
createStoreWithApi("api string", {...initialState});
And in all other files that need access to the store:
import store from "./create-store";
const state = store.getState();
As far as setting the API value. I'd recommend setting that in the store with a new reducer action. Then your middleware and other components will have access to it via const state = store.getState();. Then you don't have to worry about initializing your store with it. And your components store.dispatch(setAPI("new api string")) then store.dispatch(isLoading(true)). And your middleware making the calls can run store.getState() and get the current api value before making a backend call.

Related

Vuex/quasar - mutate state from firebase 9 on boot

I'm using quasar with firebase 9. I'm trying to set the state with actions during the boot of the project. I have data in firebase that I want to load only once and assign to an array in the Vuex state.
there might be a way in firebase 8 but I can't solve this problem in the newer version of firebase
import { getData } from "src/firebase/config";
// load data from packages collection
export const loadPackages = async () => {
const packages = await getData("packages");
return packages;
}
// load data from menu collection
export const loadMenu = async () => {
const packages = await getData("meals-menu");
return packages;
}
I use the return of each function in the respective vue component but I want to use the return in mutating the vuex state.
any tips?
thank you
as a work around solution; try saving data from firebase into localstorage then set vuex state after localstorage is set
firebase collection has event methods to allow exactly this
Look at this as starting point: https://firebase.google.com/docs/database/web/read-and-write#read_data_once_with_an_observer
Inside event handlers you have the 'realtime' changes/added/removed data
So you can change store

add more reducers after store had been created

is it possible to add reducers after the store has been created?
example :
let's say I have this react app
//App.jsx
const reducers = {foo : []}
const store = createStore(combineReducers(reducers));
const App = () => <Provider store={store}><Test/></Provider>
//Test.jsx
class Test extends Component {
componentDidMount() {
//here I want to add new reducer to the store
//I will handle duplication problem
//I'm looking for something like this
store.appendReducer('REDUCER_KEY' , someReducer);
}
...
}
export default connect()(Test)
I'm wondering if this possible, if so how to implement this.
I have an idea in mind, but it's not perfect. I can add a global callback function and listen for any call at the root component, then recreate the store when any call happens. the problem with this it will cause the whole component tree to be rerendered at any reducer addition.
yes it's possible some thing like that
const newRootReducer = combineReducers({
existingSlice: existingSliceReducer,
newSlice: newSliceReducer
})
store.replaceReducer(newRootReducer)
you can check the DOC for more info redux DOC

Alternative to getState for recurring variables in Redux

I use getState to get a clientId that I need to include in every api call right now. Problem is that this interrupts data flow as the app doesn't rerender when clientId changes. Do I have to manually get the clientId in every component that I need to include it in or is there a better alternative? (clientId is also in store and is fetched first when the user logs in)
Sounds like a good candidate for the use of Context.
Here's a fictitious example of how you can set the client ID at a high level but reference it in nested components without having to query the Redux store each time using Hooks:
App
const ClientContext = React.createContext(null);
function App(props) {
return (
<ClientContext.Provider value={props.clientId}>
<MyApiComponent />
</ClientContext>
)
}
const mapStateToProps = getState => ({
clientId: getState().clientId
})
export default connect(mapStateToProps, {})(App);
So we only need to connect the App to the store to retrieve the client ID, then using Context we can make this value accessible to nested components. We can make use of useContext to pull that value in to each component
function MyApiComponent() {
const clientId = useContext(ClientContext);
...
return <MyNestedApiComponent />;
}
function MyNestedApiComponent() {
const clientId = useContext(ClientContext);
...
}
Whether it's function or class components you are using, the principle is the same - Context is used to share global state down to nested components.

React Native: HeadslessJS and Redux - How to access store from task

We have a ReactNative app that uses redux, redux-persist and a HeadlessJS task. This task needs to have access to the store. As the task fires without booting the entire app (and so has no access by default), we thought we could simply create the store inside the task as well so that it would be rehydrated by redux-persist. It turns out, however, that the store created in this way is different from the one in the app: after running, they contain different values. We tested this in several ways and it seems indeed a problem with the stores (and not with the actions for instance)
How should we access a Redux store from an HeadlessJS task?
Relevant code:
store/configure.js:
configureStore = (client) => {
const middleware = createMiddleware(client);
const finalCreateStore = applyMiddleware(thunk, middleware, logger)(createStore);
const store = finalCreateStore(rootReducer, undefined, autoRehydrate());
return store;
};
In use (both in the app and in the service):
const client = new ApiClient();
const store = configureStore(client);
client.setStore(store);
persistStore(store, {
storage: AsyncStorage,
}
In the app we simply use the Provider from react-redux to use the store, in the service we use store.dispatch.
For people looking for solution. I have found the solution in here.
The idea is to bind the store to async method.
https://github.com/react-native-kit/react-native-track-player/issues/63
Copy pasting the solution here.
// index
const store = ...
....registerHeadlessTask('TrackPlayer', () => require('event-handler.js').bind(null, store));
// event-handler.js
module.exports = async (store, data) {
if(data.type == '...') {
store.dispatch(...);
}
};
simply create the store inside the task as well so that it would be rehydrated by redux-persist.
This did indeed happen.
You created two stores (not advisable with redux) which were both hydrate, but not linked, as there is no such thing as linked redux stores.
Every time you run createStore, it's a new store. And every time you dispatch, you do that on a specific store.
Unfortunately async or multithreaded issues are not directly addressed by redux.
It would be possible though with middleware and / or store listeners to keep the two stores in sync.
But redux is also just not a mean for communication between threads (which I assume these tasks are, or you could just give the task a reference to the store once it was created or give the main app the store reference from the task).
It's more a form of Command-Query-Separation and centralized state.
You can access your store directly as reference.
Let's say you have your headless set in index.js, then you can just simply use store there like this:
import { AppRegistry } from 'react-native';
import Store from './src/Redux/Store';
import { someAction } from './src/Redux/Actions/someActions';
import App from './App';
import { name as appName } from './app.json';
const HeadlessTask = async () => {
console.log('Receiving HeadlessTask');
const someParam = await Store.getState().Something.someParam;
if (someParam) {
Store.dispatch(someAction(someParam));
} else {
Store.dispatch(someAction());
}
};
AppRegistry.registerHeadlessTask('HeadlessTask', () => HeadlessTask);
AppRegistry.registerComponent(appName, () => App);

How do I fire a LOCATION_CHANGE event for react-router-redux?

According to the documentation I should be able to fire a LOCATION_CHANGE event by calling push('/with/the/path'). Example from docs:
import { routerMiddleware, push } from 'react-router-redux'
// Apply the middleware to the store
const middleware = routerMiddleware(browserHistory)
const store = createStore(
reducers,
applyMiddleware(middleware)
)
// Dispatch from anywhere like normal.
store.dispatch(push('/foo'))
However, this looks like when I call push I'm getting this (which doesn't look like it's doing anything):
What am I missing?
Make sure to sync your history with your store. The following is an example setup:
import { routerMiddleware, syncHistoryWithStore, push } from 'react-router-redux'
// Apply the middleware to the store
const router = routerMiddleware(browserHistory)
const store = createStore(
reducers,
applyMiddleware(router)
)
const history = syncHistoryWithStore(browserHistory, store)
// Dispatch from anywhere like normal.
store.dispatch(push('/foo'))
Notice the bit about syncHistoryWithStore after you create the store.
Hope this helps.

Categories

Resources