const fetch = url => dispatch => {
// ...
}
export const fetchQuestions = tag => (dispatch) => {
return dispatch(fetch(tag));
};
What is dispatch in the fetch function ? url is a first and single parameter fetch function. But what is dispatch here ?
This is equivalent to one function returning another. I.e. this
const fetch = url => dispatch => {
// ...
}
is equivalent to
const fetch = function(url) {
return function(dispatch) {
// ...
}
}
Similarly this
export const fetchQuestions = tag => (dispatch) => {
return dispatch(fetch(tag));
};
is equivalent to
export const fetchQuestions = function(tag) {
return function(dispatch) {
return dispatch(fetch(tag));
}
};
dispatch is the first and single parameter of the function returned by the url => ... function. With normal function syntax, it would be
const fetch = function(url) {
return function(dispatch) {...}
}
fetch is a named function expression that takes a url parameter and returns a new function that takes a dispatch parameter.
You could rewrite using traditional function syntax:
const fetch = function (url) {
return function(dispatch) {
// ...
}
}
Its a shorter way of writing a function that returns another function. The arguments url and dispatch are arguments to the curryed function The ES5 equivalent of arrow function syntax would be
function fetch(url) {
return function(dispatch) {
....
}
}
or with Arrow syntax as
const fetch = (url) => {
return (dispatch) => {
// ...
}
}
Similarly you would have fetchQuestion written as
export function fetchQuestions(tag) {
return function(dispatch){
return dispatch(fetch(tag));
}
}
or with Arrow syntax as
export const fetchQuestions = (tag) => {
return (dispatch) => {
return dispatch(fetch(tag));
}
};
Related
I'm trying to override a method of an object returned from an async function but get this error;
dataProvider.getList is not a function
i tried to extend the object simply in this way but obviously is not correct
function App() {
const [dataProvider, setDataProvider] = useState(null);
useEffect(() => {
const buildDataProvider = async () => {
//this must to be called with await
const dataProvider = await buildHasuraProvider({
clientOptions: { uri: 'http://localhost:8080/v1/graphql' }
});
//this object should be the same as above but with an overridend method (getList)
const extendeDataProvider = {
...dataProvider,
getList: (resource, params) => {
if (resource === 'apikeys') {
//implementation missing
} else {
// fallback to the default implementation
return dataProvider.getList(resource, params);
}
}
}
setDataProvider(() => extendeDataProvider);
};
buildDataProvider();
}, []);
if (!dataProvider) return <p>Loading...</p>;
return (<Admin dataProvider={dataProvider} />)
}
export default App;
i asume that the issue is with your fallback implementation in this line:
// fallback to the default implementation
return dataProvider.getList(resource, params);
u can also prove that by logging it this way -
console.log(typeof(dataProvider.getList))
I've created an Asynchronous function to call a weather API that will return the requested information submitted. The function works fine however when I call this function inside of a Class Component the result is returned twice. This isn't exactly a breaking bug but I'd rather not have two API calls occurring if not necessary and I'm curious as to why this method is being called twice in the first place.
Here is my code.
async function submitQuery(props) {
//Incase I decide to add aditional parameters such as city, country etc.. I've decided to place the zip property in an object so I can just
//add additional properties in the submissions object
const submission = {
zip: props,
};
if (!Number(props)) return console.log("this is not a number");
const { error } = await validate(submission);
if (error) return console.log(error.message);
const config = {
method: "get",
url: `https://api.openweathermap.org/data/2.5/weather?zip=${props}&appid=${apiKey}`,
headers: {},
};
/*
const query = await axios(config).then(function (response) {
const result = response.data.main;
return result;
});
*/
//console.log(query);
return 4;
}
class WeatherReport extends React.Component {
getResults = async () => {
const result = await submitQuery("08060");
console.log(result);
};
render() {
return (
<div className="reportContainer">
<WeatherCard getResults={this.getResults} />
</div>
);
}
}
const WeatherCard = ({ getResults }) => {
getResults();
return <div></div>;
};
The problem is that you're calling getResults in the render method of WeatherCard, move it to a useEffect so its not called every time
const WeatherCard = ({ getResults }) => {
React.useEffect(() => {
getResults();
}, [getResults]);
return <div></div>;
};
I am little confused about loading data approach.
So i want to show loading screen on page load until all data is loaded, my approach is to make for each fetch method on this page a loading state. here I am using redux with thunk to dispatch async action.
actions-types:
export const IS_LOADING_SKILLS = "IS_LOADING_SKILLS";
export const SET_SKILLS = "SET_SKILLS";
export const IS_LOADING_INTRO = "IS_LOADING_INTRO";
export const SET_INTRO = "SET_INTRO";
actions:
import { SET_SKILLS, SET_INTRO,IS_LOADING_INTRO,IS_LOADING_SKILLS} from "./actions-type"
export const fetchSkills = () => {
return (dispatch) => {
dispatch(isLoadingSkills(true));
await api.get(url)
.then(({ data }) => dispatch(setSkills(data)));
dispatch(isLoadingSkills(false));
}
}
export const fetchIntro = () => {
return (dispatch) => {
dispatch(isLoadingIntro(true));
await api.get(url)
.then(({ data }) => dispatch(setIntro(data)));
dispatch(isLoadingIntro(false));
}
}
const setSkills = (payload) => {
return {
type: SET_SKILLS,
payload: payload
}
}
const setIntro = (payload) => {
return {
type: SET_INTRO,
payload: payload
}
}
const isLoadingSkills = (payload)=>{
return{
type:IS_LOADING_SKILLS,
payload:payload
}
}
const isLoadingIntro = (payload)=>{
return{
type:IS_LOADING_INTRO,
payload:payload
}
}
state:
const InitialState ={
loading:{
loadingIntro:false,
loadingSkills:false
},
data:{
intro:"",
skills:[],
}
}
now when both loading state for each fetch data method become false the loading screen will disappear.
I want to know if this is a good approach or what is better please explain. thank you!
This is a good approach, but your code has some weird parts. Either use async/await or .then/.catch callbacks (don't forget about the .catch, you could dispatch a setErrorMessage action or so).
So, with promises you would do:
export const fetchSkills = () => {
return (dispatch) => {
dispatch(isLoadingSkills(true));
api.get(
.then(({ data }) => {
dispatch(setSkills(data)));
dispatch(isLoadingSkills(false));
})
.catch((error) => ...stuff)
}
}
export const fetchIntro = () => {
return (dispatch) => {
dispatch(isLoadingIntro(true));
api.get(url)
.then(({ data }) => {
dispatch(setIntro(data)));
dispatch(isLoadingIntro(false));
})
.catch((error) => ...stuff)
}
}
And with async/await you would do:
export const fetchSkills = () => {
return async (dispatch) => {
try {
dispatch(isLoadingSkills(true));
const { data } = await api.get(url)
dispatch(setSkills(data)));
dispatch(isLoadingSkills(false));
} catch(error) {
...stuff
}
}
}
export const fetchIntro = () => {
return async (dispatch) => {
try {
dispatch(isLoadingIntro(true));
const { data } = await api.get(url)
dispatch(setIntro(data)));
dispatch(isLoadingIntro(false));
} catch(error) {
...stuff
}
}
}
Remember that async/await is just syntactic sugar for promises. Instead of using .then and .catch on a promise you just define the function using await as async (which allows you to use await AND returns a promise), and you will need to try/catch to catch any errors that can happen whilst doing the request.
I have a function:
const fetchMovies = (function (query) {
const requestId = this.requestId(query)
return dispatch => {
dispatch(sendingRequest(requestId))
return ajax.get(`/movies/search?q=${query}`)
.then(res => {
dispatch(receievedResponse(requestId))
return dispatch(addMovies(res.data.movies))
})
}
}).bind({
requestId: (query) => `fetchMoviesLoading-${query}`
})
This allows the fetchMovies function to have the requestId able to be called within itself. However, the requestId property cannot be accessed like so:
fetchMovies.requestId === undefined // true
Is there a simple/clean way to expose the requestId?
This just looks messy:
const fetchMoviesContext = {
requestId: (query) => `fetchMoviesLoading-${query}`
}
const fetchMovies = (function (query) {
const requestId = this.requestId(query)
return dispatch => {
dispatch(sendingRequest(requestId))
return ajax.get(`/movies/search?q=${query}`)
.then(res => {
dispatch(receievedResponse(requestId))
return dispatch(addMovies(res.data.movies))
})
}
}).bind(fetchMoviesContext)
fetchMovies.requestId = fetchMoviesContext.requestId
Just use
function fetchMovies(query) {
const requestId = fetchMovies.requestId(query)
// ^^^^^^^^^^^
return dispatch => {
dispatch(sendingRequest(requestId))
return ajax.get(`/movies/search?q=${query}`)
.then(res => {
dispatch(receievedResponse(requestId))
return dispatch(addMovies(res.data.movies))
})
}
}
fetchMovies.requestId = (query) => `fetchMoviesLoading-${query}`;
Don't overcomplicate it.
I want to pass in a boolean value as the 2nd argument to my actionCreator which would determine what my middleware dispatches, but how do I give my middleware access to this 2nd argument?
Do I have to dispatch an array or object instead of a promise?
export const fetchPokemon = function (pokemonName, booleanValue) {
return function (dispatch) {
dispatch({type: 'REQUESTING'})
const requestURL = `http://pokeapi.co/api/v2/pokemon/${pokemonName}/`
dispatch(fetch(requestURL))
}
}
Middleware
const fetchPromiseMiddleware = store => next => action => {
if (typeof action.then !== 'function') {
return next(action)
}
...
return response.json()
}).then(function (data) {
if booleanValue {
store.dispatch(receivePokemon(formatPokemonData(data)))
} else {
store.dispatch(fetchPokemonDescription(data.name))
}
})
}
it seems you have answered yourself, the action you dispatch should contain all the relevant data.
The simplest option seem to be to add a property (or properties) to your action, as a Promise is already an object.
export const fetchPokemon = function (pokemonName, booleanValue) {
return function (dispatch) {
dispatch({type: 'REQUESTING'})
const requestURL = `http://pokeapi.co/api/v2/pokemon/${pokemonName}/`
dispatch(Object.assign(fetch(requestURL), {
someNameForYourBooleanParameter: booleanValue
})
}
}
and
const fetchPromiseMiddleware = store => next => action => {
if (typeof action.then !== 'function') {
return next(action)
}
...
return response.json()
}).then(function (data) {
if (action.someNameForYourBooleanParameter) {
store.dispatch(receivePokemon(formatPokemonData(data)))
} else {
store.dispatch(fetchPokemonDescription(data.name))
}
})
}
If you want to continue this path, I'd recommend to put these values under a .payload property to prevent any collision with members of the Promise class
I'd take this approach further to avoid the multiple actions being dispatched for the same logical action:
export const fetchPokemon = function (pokemonName, booleanValue) {
return function (dispatch) {
const requestURL = `http://pokeapi.co/api/v2/pokemon/${pokemonName}/`;
dispatch({
type: 'REQUESTING',
promise: fetch(requestURL),
payload: {
someNameForYourBooleanParameter: booleanValue
}
})
}
}