export const postMoviePopular = url = dispatch => {
const data = axios.get(url);
dispatch(saveMoviePopular(data));
}
const saveMoviePopular = payload => {
return {
type: POST_MOVIE_POPULAR,
payload
}
}
This is my code which does not work because it is curried, but if it IS NOT curried like below, it works, why is this??
export const postMoviePopular = url => {
return dispatch => {
const data = axios.get(url);
dispatch(saveMoviePopular(data));
}
}
I'm wondering if it has anything to do with the way I am calling mapDispatchToProps???
componentDidMount() {
this.props.postMDBConfig(`https://api.themoviedb.org/3/configuration?api_key=${this.props.apiKey}`);
this.props.postMoviePopular(`https://api.themoviedb.org/3/movie/popular?api_key=${this.props.apiKey}&language=en-US&page=1®ion=US`)
}
const mapDispatchToProps = (dispatch) => {
return {
postMDBConfig: url => dispatch(postMDBConfig(url)),
postMoviePopular: url => dispatch(postMoviePopular(url))
}
}
You need a return statement. And another arrow.
export const postMoviePopular = url => dispatch => {
const data = axios.get(url);
return dispatch(saveMoviePopular(data));
}
Related
i'm newbie here, i'm stuck. i want to change value from false to true, to stop shimmering when data sucessfully to load.
i have action like this
import axios from "axios";
import { CONSTANT_LINK } from "./constants";
import { GET } from "./constants";
import { ERROR } from "./constants";
import { connect } from 'react-redux';
export const addData = () => {
return (dispatch) => {
axios
.get(CONSTANT_LINK)
.then((res) => {
dispatch(addDataSuccess(res.data));
})
.catch((err) => {
dispatch(errorData(true));
console.log("error");
});
};
};
const addDataSuccess = (todo) => ({
type: GET,
payload: todo,
});
const errorData = (error) => ({
type: ERROR,
payload: error,
});
and this is my homepage which influential in this matter
const [shimmerValue, setShimmerValue] = useState(false)
useEffect(() => {
setShimmerValue(true)
dispatch(addData());
}, []);
<ShimmerPlaceholder visible={shimmerValue} height={20}>
<Text style={styles.welcomeName}>Welcome,Barret</Text>
</ShimmerPlaceholder>
i dont understand how it works
You can pass callback like this
const [shimmerValue, setShimmerValue] = useState(false);
const updateShimmerValue = () => {
setShimmerValue(true);
}
useEffect(() => {
// setShimmerValue(true) // remove this from here
dispatch(addData(updateShimmerValue)); // pass callback as param here
}, []);
Callback call here like
export const addData = (callback) => {
return (dispatch) => {
axios
.get(CONSTANT_LINK)
.then((res) => {
....
callback(); // trigger callback like this here
})
.catch((err) => {
....
});
};
};
you can use it:
const [shimmerValue, setShimmerValue] = useState(false)
useEffect(() => {
setState(state => ({ ...state, shimmerValue: true }));
dispatch(addData());
}, [shimmerValue]);
I want to make static generation for top products with getStaticProps.
now a section of my rendering is not needed to static generation. for example: comments, related products.
full code:
export default function Gift(props) {
let [relatedProducts, setRelatedProducts] = useState([]);
const getRelatedProducts = () => {
api.get(`gift/id/${props.id}/relateds/count/10`).then(res => {
console.log(res.data.data);
setRelatedProducts(res.data.data)
})
}
//called n times. looping !!!
getRelatedProducts();
return (
<GiftProvider value={props}>
<ProductPage/>
<RelatedProducts title="related products" products={relatedProducts}/>
<ProductGeneralProperties/>
<ProductComment/>
</GiftProvider>
);
}
export async function getStaticPaths() {
const gifts = await getTopGifts()
const paths = gifts.map((gift) => ({
params: {slug: gift.slug}
}))
return {paths, fallback: 'blocking'}
}
export async function getStaticProps(context) {
const slug = context.params.slug
const gift = await getGiftWithSlug(slug)
return {
props: gift,
}
}
but with below code my codes renders multi times:
export default function Gift(props) {
let [relatedProducts, setRelatedProducts] = useState([]);
const getRelatedProducts = () => {
api.get(`gift/id/${props.id}/relateds/count/10`).then(res => {
console.log(res.data.data);
setRelatedProducts(res.data.data)
})
}
getRelatedProducts();
You can use useEffect hook to call the api
useEffect(() => {
const getRelatedProducts = () => {
api.get(`gift/id/${props.id}/relateds/count/10`).then(res => {
console.log(res.data.data);
setRelatedProducts(res.data.data)
})
}
getRelatedProducts();
},[])
Is it an acceptable practice to have two custom react hooks in the same component, one after another?
The issue I am dealing with is as follows:
The first custom hook useBudgetItems will load, but the subsequent one will be undefined. I think I understand why it's happening (my budgetSettings property inside my useBudgetSettings loads after the console.log() statement), but I am not sure how to get around this and whether this is the right approach.
const BudgetCost ({ projectId }) => {
const { budgetCost, loaded } = useBudgetCost({ key: projectId });
const { budgetSettings } = useBudgetSettings({ key: projectId });
const [totalBudget, setTotalBudget] = useState(budgetCost.totalBudget);
const [budgetCosts, setbudgetCosts] = useState(budgetCost.items);
// This will be undefined
console.log(budgetSettings)
if(!loaded) return <div/>
return (
...
...
)
});
My useBudgetCost custom hook is as follow (the useBudgetSettings isn't much different in the mechanics.
const useBudgetCost = ({ key, notifyOnChange }) => {
const [loaded, setIsLoaded] = useState(false)
const { budgetCost, setBudgetCost } = useContext(ProjectContext)
useEffect(() => {
if(key)
return getBudgetCost(key);
},[key]);
const getBudgetCost = (key) => {
let { budgetCosts, loaded } = UseBudgetCostsQuery(key);
setBudgetCost(budgetCosts);
setIsLoaded(loaded);
}
let onBudgetCostChange = (update) => {
let tempBudgetCostItems = copyArrayReference(budgetCost);
tempBudgetCostItems = {
...tempBudgetCostItems,
...update
}
setBudgetCost(tempBudgetCostItems)
if (notifyOnChange)
notifyOnChange(update)
}
return {
loaded,
budgetCost,
onBudgetCostChange
}
}
useBudgetSettings component:
const useBudgetSetting = ({ key }) => {
const [loaded, setIsLoaded] = useState(false)
const { budgetSettings, setBudgetSettings } = useContext(ProjectCondext)
const globalContext = useContext(GlobalReferenceContext);
useEffect(() => {
if(key)
return getBudgetSetting(key);
},[key]);
const getBudgetSetting = (key) => {
let { budgetSettings, loaded } = UseBudgetSettingsQuery(key);
console.log(budgetSettings);
setBudgetSettings(budgetSettings);
setIsLoaded(loaded);
}
const getBudgetReferences = (overrideWithGlobal = false) => {
if(overrideWithGlobal)
return globalContext.getBudgetReferences();
return budgetSettings.map((item) => { return { value: item.key, label: item.costCode } });
}
const getCategoryText = (key) => _.get(_.find(getBudgetReferences(), (bc) => bc.value === key), 'label');
return {
loaded,
budgetSettings,
getCategoryText,
getBudgetReferences
}
}
So decided to use redux-thunk and I have a problem to write a function in my actions and reducer. Actually function looks like this:
async getData() {
if (this.props.amount === isNaN) {
return;
} else {
try {
await fetch(
`https://api.exchangeratesapi.io/latest?base=${this.props.base}`,
)
.then(res => res.json())
.then(data => {
const date = data.date;
const result = (data.rates[this.props.convertTo] * this.props.amount).toFixed(4);
this.setState({
result,
date,
});
}, 3000);
} catch (e) {
console.log('error', e);
}
}
}
Also I already have action types
export const FETCH_DATA_BEGIN = 'FETCH_DATA_BEGIN';
export const FETCH_DATA_SUCCESS = 'FETCH_DATA_SUCCESS';
export const FETCH_DATA_FAIL = 'FETCH_DATA_FAIL';
and actions like this
export const fetchDataBegin = () => {
return {
type: actionTypes.FETCH_DATA_BEGIN,
};
};
export const fetchDataSuccess = data => {
return {
type: actionTypes.FETCH_DATA_SUCCESS,
data: data,
};
};
export const fetchDataFail = error => {
return {
type: actionTypes.FETCH_DATA_FAIL,
error: error,
};
};
And then comes the hard part for me where I don't know how to get the same result from function async getData(). I already have just this in my action :
export async function fetchData() {
return async dispatch => {
return await fetch(`https://api.exchangeratesapi.io/latest?base=${this.props.base}`)
.then(res => res.json())
.then(data => {
// <------------------- WHAT NEXT?
}
};
export function fetchData() {
return dispatch => {
fetch(`https://api.exchangeratesapi.io/latest?base=${this.props.base}`)
.then(res => res.json())
.then(data => dispatch(fetchDataSuccess(data)), e => dispatch(fetchDataFail(e)))
}
};
Now this code:
const date = data.date;
const result = (data.rates[this.props.convertTo] * this.props.amount).toFixed(4);
this.setState({
result,
date,
});
goes into your reducer
if(action.type === FETCH_DATA_SUCCESS) {
const date = action.data.date;
const rates = action.data.rates;
return { ...state, rates, date };
}
Now you can use the redux state in your component and make the rest of the calculations there (ones that need this.props).
To dispatch the fetchData action now, you do this.props.dispatch(fetchData()) in your react-redux connected component.
EDIT
Here's how you use the state in the component.
I'm assuming you have created the redux store. something like:
const store = createStore(rootReducer,applyMiddleware(thunk));
Now, you can use the react-redux library's connect function to connect the redux state to your component.
function mapStateToProps(state, ownProps) {
return {
date: state.date,
result: (state.rates[ownProps.convertTo] * ownProps.amount).toFixed(4);
}
}
function mapDispatchToProps(dispatch) {
return {
fetchData: () => dispatch(fetchData())
}
}
export default connect(mapStateToProps,mapDispatchToProps)(YourComponent)
You can use this Higher Order Component in your DOM now and pass the appropriate props to it:
import ConnectedComponent from "./pathTo/ConnectedComponent";
...
return <View><ConnectedComponent convertTo={...} amount={...} /></View>;
And, also inside YourComponent you can now read this.props.date and this.props.result and use them wherever you need to.
You might want to look at selectors in the future to memoize the state and reduce the performance cost of redux.
I have several routes that use the same controller:
<Route component={Search} path='/accommodation(/:state)(/:region)(/:area)' />
and when the route is changed I call the api function from within the component:
componentWillReceiveProps = (nextProps) => {
if (this.props.params != nextProps.params) {
loadSearch(nextProps.params);
}
}
which is an action as follows:
export function loadSearch (params) {
return (dispatch) => {
return dispatch(
loadDestination(params)
).then(() => {
return dispatch(
loadProperties(params)
);
});
};
}
which loads:
export const DESTINATION_REQUEST = 'DESTINATION_REQUEST';
export const DESTINATION_SUCCESS = 'DESTINATION_SUCCESS';
export const DESTINATION_FAILURE = 'DESTINATION_FAILURE';
export function loadDestination (params) {
const state = params.state ? `/${params.state}` : '';
const region = params.region ? `/${params.region}` : '';
const area = params.area ? `/${params.area}` : '';
return (dispatch) => {
return api('location', {url: `/accommodation${state}${region}${area}`}).then((response) => {
const destination = formatDestinationData(response);
dispatch({
type: DESTINATION_SUCCESS,
destination
});
});
};
}
export const PROPERTIES_REQUEST = 'PROPERTIES_REQUEST';
export const PROPERTIES_SUCCESS = 'PROPERTIES_SUCCESS';
export const PROPERTIES_FAILURE = 'PROPERTIES_FAILURE';
export function loadProperties (params, query, rows = 24) {
return (dispatch, getState) => {
const locationId = getState().destination.id || 99996;
return api('search', {locationId, rows}).then((response) => {
const properties = response.results.map(formatPropertiesData);
dispatch({
type: PROPERTIES_SUCCESS,
properties
});
});
};
}
On initial page load this works and returns data from an api and renders the content. However on changing the route, the loadSearch function is fired but the dispatch (which returns the actual data) doesn't.
Please change your code to this. You missed a dispatch.
Assumption : You are using redux-thunk, and the component has access to dispatch via props (connected). Since you mentioned that you are dispatching on page load, I think this is the case.
componentWillReceiveProps = (nextProps) => {
const {dispatch} = this.props;
if (this.props.params != nextProps.params) {
nextProps.dispatch(loadSearch(nextProps.params));
}
}