React - How do I pass a search criteria object from my component to its epic while fetching data from service? - redux-observable

I am working on an application using React with Redux Observable.
I have a react Component which builds a seachCriteria object taking inputs from users (Dropdown, date picker).
On Click of a button I call a service which is part of an Epic.
I want to pass this search Criteria Object as a body parameter in my fetch request.
What's the best way to do that. Since epic takes an action and a store, shall I add that search criteria object as a part of store (my state object) ?

On Click of a button I call a service which is part of an Epic. I want to pass this search Criteria Object as a body parameter in my fetch request.
Since redux-observable plugs into redux, you should be dispatching an action rather than calling a service directly.
By doing that you could add your search criteria to the action payload and your epic would look like:
const searchEpic = action$ => action$.pipe(
ofType('search'),
pluck('payload'),
map(toQueryString),
mergeMap(query => fetch(`api.something.com/search?${query}`)),
mergeMap(result => result.json()),
map(data => ({ type: 'search resolved', payload: data })),
);

Related

Vue 3 composition API, undefined variable, lifecycle

I am quite new to Vue.js and while developing my first project i have stumbled upon a problem which I think is related to the component's lifecycle.
The context is the following:
I am using Vue 3 with composition API.
I have a "Map" component in which I use d3.js to show a chart.
In my Setup() method I have onBeforeMount() and onMounted().
In onBeforeMount() method, I want to use data from my Firestore database for the values in the chart. The console.log on line 47 shows the data correctly.
In onMounted(), is where I plan to put my d3 chart. But when I try to access the data as in console.log on line 55, I get undefined...
Ideally, from "Map" component, I want to fetch the data from my database and use it in order to create the chart and then have the component render the chart.
Then I would have another component called 'MapSettings" that will be able to alter the data in "Map" such as filters etc...and have the values automatically updated in the chart.
Finally, both components will be siblings and have same parent component. So "Map" and "MapSettings" are on same hierarchical level.
But first I need to understand why I am getting undefined.. Any help or suggestion would be greatly appreciated!
Your lifecycle hooks look nice. The problem that you're facing has to do with code been executed asynchronously and code been executed synchronously.
You're declaring a function that uses async-await feature. This function will be executed asynchronously. In this case, you're getting data from Firestore and storing it in a ref at onBeforeMount().
On the other hand, you're declaring a normal function at onMounted() trying to access a ref's value, which will result in undefined because the function that you define at onBeforeMount() didn't finish its execution (or it's in the event queue) when onMounted is called.
That's why you first see the console.log that comes from onMounted.
One solution would merge both functions in one lifecycle hooks and use async await:
setup() {
const {actorDocs, load} = getActorDocs()
const actorsData = red([])
// load actor data from db
onBeforeMount( async () => {
await load()
actorsData.value = actorDocs
console.log(actorsData.value)
// manipulate it here...
})
}
Keep in mind that you cannot pause with async-await a lifecycle hook. What you're pausing is the function that Vue will execute in that hook. Indeed, this is really nice because pausing a hook wouldn't be efficient at all.
i face that problem, in my case i want use imgsRef.value out of onBeforeMount scope. How to get imgsRef value out of beforeMount scope?
onBeforeMount( async () => {
await axios
.get("http://localhost:3000/ourMoment")
.then((response) => {
imgsRef.value = response.data
// imhsRef get the value from api
console.log(imgsRef.value.photo)
})
})
// i try to concole.log here but the value still empty
console.log(imgsRef.value)

get relevant value from useSelector

I have two hooks:
const dispatch = useDispatch();
const response = useSelector(state => state.responseData);
And submit function:
const submit = () => {
dispatch(connectToServer(`${BASE_URL}user/signIn`, {
email: state.email,
password: state.password
}, 'post'))
if (response.data.token) <--- this data is 1 step late
//do smth
I see relevant data only in JSX elements when they are rendered, but there is no way to make a function based on this data, this data is 1 step late.
There's three issues here:
The connectToServer action is presumably doing some async work, so there's no way a response can have been updated by the time the next line runs
Even if you do await dispatch(connectToServer()), React may not have re-rendered yet
The callback can only reference the variable values that existed in scope at the time the callback was defined, ie, the render before the user clicked the "Submit" button.
You'll need to either:
Move the response handling into the thunk itself
Have the thunk retrieve the updated data from the store and return it / use it somehow
Move the response token handling into a useEffect and wait for the next re-render that has the right data

Applying filters to nested components

I'm trying to apply a custom filter (for adding properly inflected suffixes in Turkish) using the <Trans> component and interpolation. The problem is that a key part of the sentence that needs to be inflected (the name of a user), is rendered by its own component. Is there a way to conditionally apply a filter to a nested component?
I can't directly use the inflection filter on the inner component. The inner component is used in multiple places, and each context may need different filters or none at all.
The inner component just gets a user id as a prop, and fetches its own contents (the user's name) through react-redux. The outer component (which owns the sentence context), has no idea about the contents of the inner component.
The filters are useful for a single language (Turkish). The filter to be used must be part of the translation file, as all other languages will not use those filters. (Also, choosing a filter is the job of the translator)
Practically it would help if I could do something like that (but that doesn't work)
{ // en.json
newMessage: "You got a new message from <1>user</1>"
}
{ // tr.json
newMessage: "Size {{<1>user</1>, ablative_suffix}} yeni bir mesaj geldi"
}
Which I wish would render something like "Size Mert'ten yeni bir mesaj geldi" given the user name "Mert". Is there another way to accomplish something like this?
<User>
const User = ({ user }) => <strong>{user ? user.name: null}</strong>;
const mapStateToProps = (state, { id }) => ({ user: state.users[id] });
export default connect(mapStateToProps);
<NewMessage>
export const NewMessage = ({userId}) =>
<Trans i18nKey="newMessage">
You got a new message from <User id={userId}/>
</Trans>;
Checkout this solution, https://codesandbox.io/s/react-i18next-2w5c2.
I'm extracting the children on the User component (hacky way) and add it as special value _1_children, then, use it translations, which Trans will inject it as children to the component, therefore, there is children || 'data' as the children of User component.
From all of my attempts, looks like there is not "clean" way to do so, I think that it is better to connect to the redux store by your self (you have the id) and pass the user name as variable like I did.
Anyway, i18next Trans component supports only injecting data from translation to Component and not vice versa.

Watch for URL query parameter in Vuex store

I am using Nuxt.js with Vuex and I would like to trigger a mutation when somebody enters in my web with a certain parameter (ex: https://example.com/?param=abc), and pass the parameter to a state.
I tried to check the documentation of the watchQuery property https://nuxtjs.org/api/pages-watchquery, but there’s no examples about how to do this, I just found this How to watch on Route changes with Nuxt and asyncData but I can’t see any way of how to write an action in Vuex store with watchQuery.
I tried writing:
actions: {
watchQuery: true,
asyncData ({ query, app }) {
const { start } = query
const queryString = start ? `?start=${start}` : ''
return app.$axios.$get(`apps/${queryString}`)
.then(res => {
commit('setParam',res.data);
})
},
}
But that syntax is not allowed.
Any help would be welcome, thanks in advance!
From my understanding watchQuery sets a watcher for query string, meaning it's waiting for the query to change while the page is already rendered making it possible to call methods like asyncData() again.
Since you only want to save a certain parameter when the user enters the page and then pass the paramater to a state you just need to move your asyncData method to a page from which you want to get the parameter, you will also need to extract store and query from the context automatically passed into asyncData and then using the store and query save the query parameter into your state.
Here is a simple demonstrantion
// Your page from which you want to save the param
export default {
asyncData({store, query}) { // here we extract the store and query
store.state.somethingForSavingTheParam = query.nameOfTheParamYouWantToSave
// instead of using store.state you could use store.commit(...) if that's what you want
}
}

How to refresh component in reactnative using redux pattern?

I am using Redux pattern, I have problem in state management.
Condition: When i successfully submitted form data from action.js with actonTypes.SUBMITSUCCESS. and i update view according to the status send from reducer.The problem is that when i press back from that component and again navigate to UpdateDate component the submit status remain same and every time it shows success whenever i navigate to that component.
in your reducer, after successfully sending the form data, is the state set back to its initial value?
also, I like to create an action "clearForm" and call it in the componentWillMount() to set the state back to is initial value:
componentWillMount() {
this.props.clearEntityForm();
}