My app successfully gets API data and puts it to Redux state tree.
{
"coord":{
"lon":-0.13,
"lat":51.51
},
"weather":[
{
"id":311,
"main":"Drizzle",
"description":"drizzle rain",
"icon":"09d"
},
{
"id":501,
"main":"Rain",
"description":"moderate rain",
"icon":"10d"
}
],
//--------
//--------
"id":2643741,
"name":"London",
"cod":200
}
Props.data has been passed to components but in reality I have an access only to the
first key. For example props.data.name, props.data.id are accesiible. But props.data.coord.lon
and props.data.weather.map(---), are undefined.
Please, what's wrong with my understanding of using API dataset?
Component
export const DayItem = (props) => {
return (<MuiThemeProvider>
<Paper zDepth={2}>
{props.data.coord.lon} // No way!
{props.data.name} // OK!
</Paper>
</MuiThemeProvider>)}
Saga that gets data and dispatches an action. Puts data to Redux store.
function* getPosition() {
const getCurrentPosition = () => new Promise(
(res, rej) => navigator.geolocation.getCurrentPosition(res, rej))
// Gets user's current position assigned to const
const pos = yield call(getCurrentPosition);
const {latitude, longitude} = pos.coords;
// Yields the forecast API by user coordinates
const data = yield call(getForecastByCoords, latitude, longitude)
// Yields user's local forecast to the reducer
yield put({
type: LOAD_DATA_SUCCESS,
data
});
}
And mapStateToProps
function mapStateToProps(state) {
return {
chips: state.chipsReducer,
data: state.dataReducer
}
};
dataReducer
export const dataReducer = (state = {}, action) => {
switch (action.type) {
case LOAD_DATA_SUCCESS:
return action.data;
default:
return state;
}
};
Eventually, I got the point.
The problem was in the difference of speed React rendering vs Data loading.
Loading is always behind rendering. So, the complete set of data had no existence.
Just conditional rendering made my day {this.state.isLoading ? <div>Loading</div> : <DayItem {...this.props}/>}
you must use MapStateToProps, then use componentWillReceiveProps(nextProps)
Something like this
function mapStateToProps(state) {
return {
data:state.toJS().yourReducer.data,
}
then do next:
componentWillReceiveProps(nextProps) {
if (nextProps.data) {
if (nextProps.data.coord.lon != undefined) {
this.setState({yourData:nextProps.data.coord.lon}
}
}
Related
i got two values i.e.company and id from navigation.
let id = props.route.params.oved;
console.log("id-->",id);
let company = props.route.params.company;
console.log("company--->",company);
i got two values as a integer like this:--
id-->1
comapny-->465
Description of the image:---
if i am giving input 1 in that textInput and click on the card(lets say first card i.e.465 then i am getting those two values in navigation as in interger that i have mention above.so each time i am getting updated values.
i am getting updated values from navigation.
so i want to store those values in redux.
action.js:--
import { CHANGE_SELECTED_COMPANY } from "./action-constants";
export const changeCompany = (updatedCompany, updatedId) => {
return {
type: CHANGE_SELECTED_COMPANY,
updatedCompany,
updatedId,
};
};
reducer.js:--
import { CHANGE_SELECTED_COMPANY } from "../actions/action-constants";
const initialState = {
company: "",
id: "",
};
const changeCompanyReducer = (state = initialState, action) => {
switch (action.type) {
case CHANGE_SELECTED_COMPANY:
return {
company: {
company: action.updatedCompany,
id: action.updatedId,
},
};
}
return state;
};
export default changeCompanyReducer;
congigure-store.js:--
import changeCompanyReducer from "./reducers/change-company-reducer";
const rootReducer = combineReducers({changeCompanyReducer});
How can i store the update values getting from navigation in Redux?
could you please write code for redux??
in the component create a function that updates the values
const updateReducer = () => {
dispatch(changeCompany(props.route.params.oved, props.route.params.company))
}
then call the function in react navigation lifecycle event
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
updateReducer()
});
return unsubscribe;
}, [navigation])
its possible that a better solution would be to update the reducer before the navigation happens and not pass the data in the params but rather pull it from redux but this is the answer to the question as asked
So I have this sidebar component where I load my store and my dispatcher
//select
const mapStateToProps = state => {
return { renderedEl: state.renderedEl }
}
function mapDispatchToProps(dispatch) {
return{
renderLayoutElement: element => dispatch(renderLayoutElement(element))
}
}
Then inside the same component this Is how I trigger the dispatcher
renderEl = (el) => {
var elementName = el.target.getAttribute('id');
var renderedElements = this.props.renderedEl; //this is data from the store
for (let key in renderedElements) {
if (key == elementName) {
renderedElements[key] = true
}
}
this.props.renderLayoutElement({renderedElements});
}
Then as I understand it gets sent to the reducer
import {RENDER_LAYOUT_ELEMENT} from "../constants/action-types"
const initialState = {
renderedEl: {
heimdall: false,
skadi: false,
mercator: false
}
}
function rootReducer(state = initialState, action){
if(action.type === RENDER_LAYOUT_ELEMENT){
return Object.assign({},state,{
renderedEl: state.renderedEl.concat(action.payload)
})
}
return state
}
export default rootReducer;
This is its action
import {RENDER_LAYOUT_ELEMENT} from "../constants/action-types"
export function renderLayoutElement(payload) {
return { type: RENDER_LAYOUT_ELEMENT, payload }
};
Now the thing is. Im receiving a
state.renderedEl.concat is not a function at rootreducer / at dispatch
I dont understand why does that happen.
Becuase, actually the store gets updated as I can see, but the console returns that error. And I have to reload the render that uses the props of that store (with an onhover) in order to be able to see the changes. It doesnt happen automatically as it would happen with a state
if(action.type === RENDER_LAYOUT_ELEMENT){
return { ...state, renderedEl: { ...state.renderedEl, ...action.payload } };
}
Duplicate from comments maybe it can be helpful to someone else :)
I am using tmdb and redux-thunk to make async calls and return the data. I've tried every way possible and only empty objects show up in my store even though when I log the results I see the data there.
I tried to map through the results with no luck. If I return it by the index for example (example[0]) it does show the results of the first index.
Screenshots of store and console - https://imgur.com/a/zrv0Sjm
export const fetchVideoKeys = urlArray => {
return dispatch => {
dispatch(isLoading(true));
axios
.all(urlArray)
.then(
axios.spread((top, pop, up, now) => {
console.log(top)
dispatch(getVideoKeys(top));
})
);
};
};
const initialState = {
videoKeys: {
topRated: [],
popular: [],
upcoming: [],
nowPlaying: [],
}
};
export default function VideoTrailerReducer(state=initialState, action) {
switch (action.type) {
case VideoTrailerActionTypes.GET_VIDEO_KEYS:
return {
videoKeys: {
topRated: [action.payload]
}
}
default:
return state;
}
}
useEffect(() => {
movieIds.popular.length > 1 &&
movieIds.topRated.length > 1 &&
movieIds.upcoming.length > 1 &&
movieIds.nowPlaying.length > 1 &&
setTimeout(() => {
dispatch(
fetchVideoKeys([
createUrls(movieIds.topRated, videoUrls),
createUrls(movieIds.popular, videoUrls),
createUrls(movieIds.upcoming, videoUrls),
createUrls(movieIds.nowPlaying, videoUrls)
])
);
}, 1000);
}, [
movieIds.topRated,
movieIds.popular,
movieIds.nowPlaying,
movieIds.upcoming
]);
export const getVideoKeys = data => {
return {
type: VideoTrailerActionTypes.GET_VIDEO_KEYS,
payload: data
}
}
I expect the store to show the results but is only returning empty objects.
Based on your screengrab, I expect you wish to dispatch data.results from your ajax response. Right now you are dispatching the promise.
Try playing around with console.log(top.data.results)
Your code example assumes that your url list will have just 4 array items, so where you have this:
axios.spread((top, pop, up, now) => {
top will be the response from the first url passed.
A codepen calling your api might help.
[Also you're exposing your api key in the image]
I am working on a ReactJS 15 project using sagas and reselect as middle ware to fetch data. I can successfully get data in componentWillReceiveProps and set the data in the state, however there still not data in the render function first run when I take the data from the state. Anyone knows what's going on here? BTW, I used json-server as mock data server.Below is part of my code:
Component:
constructor(props) {
super(props);
this.state = {
timelineData: [],
};
}
componentDidMount() {
// use react redux to make the api call here
this.props.fetchTimelineData({
id: parse(this.props.location.search.substr(1)).id,
});
}
componentWillReceiveProps(nextProps) {
console.log('nextProps', nextProps);
// Successfully take the data from the nextProps (timelineData is not [])
const { timelineData } = nextProps;
this.setState({
timelineData,
});
}
render() {
// the first render called timelineData is an empty array, which will not able to populate the UI
// which RaiseTimeline is empty
const { timelineData } = this.state;
return (
<RaiseTimelineStyled>
<RaiseDetailsGrid>
<Accordion
title={
<RaiseAccordionHeader image={image} title={'Timeline'} />
}>
<Timeline.wrapper>
<RaiseTimelineStyled.divider>
<RaiseTimelineStyled.container>
<RaiseTimeline timelineEvents={timelineData} />
action.js (works fine):
export const setTimelineData = timelineData => console.log('actions.js', timelineData) || ({
type: ACTIONS.SET_TIMELINE_DATA,
timelineData,
});
Api.js (works fine):
class TimelineAPI {
// payload will be used after backend done
static fetchTimelineData(payload) {
return http.get(`${baseURI}/timeline`).then(result => console.log('api', result.data) || result.data);
}
}
Reducers: (works fine)
function TimelineDataReducer(state = initialState, action) {
switch (action.type) {
case ACTIONS.SET_TIMELINE_DATA:
console.log('reducer', action.timelineData);
return state.set('numbers', action.timelineData);
default:
return state;
}
}
Sagas: (works fine)
export function* fetchTimelineData(action) {
yield put(togglePendingScreen(true));
const { result, error } = yield call(TimelineAPI.fetchTimelineData, action.payload);
if (error) {
yield put(
toggleErrorModal({
isOpen: true,
text: error.code,
source: 'Fetch Timeline Data',
}),
);
} else {
console.log('Sagas', result.timeline);
yield put(ACTIONS.setTimelineData(result.timeline));
}
yield put(togglePendingScreen(false));
}
Selectors(works fine):
import { createSelector } from 'reselect';
const selectTimelineData = state => state.get('TimelinePageData').toJS();
const selectTimeline = () =>
createSelector(selectTimelineData, TimelineDataState => TimelineDataState.numbers);
export { selectTimeline };
To me it seems logical that you have no data on the first run.
The reason is there render() function is called once before the componentDidMount() in the react life cycle. (V15)
Look here for the react 15 life cycle : https://gist.github.com/bvaughn/923dffb2cd9504ee440791fade8db5f9
I got the answer, everything is correct, but another person name but another person name same component and set its state in side the constructor, so it's not able to render in the first time
I have a component where toggle buttons are dynamically generated. Right now, I am just trying to get it working at a basic level so you click on a button and it adds a key/value pair to the cuts = {}.
After clicking on multiple buttons the cuts should have several key/value pairs: it does in the component where cuts resides, it does in the action, and it does in the Redux store via console.log(state.cuts).
However, after mapStateToProps it is only showing the first value and I am not sure why.
Anyway, here is my code and the flow as it is initiated by the user:
// bq_cuts.js component
constructor(props) {
super(props);
this.state = {
cuts: {}
}
}
onCutSelect(cut) {
const { bqResults } = this.props;
const { cuts } = this.state;
let key = cut.name;
let value = cut.value;
cuts[key] = value;
this.setState({
cuts
})
console.log(cuts); // shows all of the selected cuts here
bqResults(cuts);
}
// results.js actions
export function bqResults(results) {
console.log(results); // shows all of the selected cuts here
return function(dispatch) {
dispatch({
type: FILTER_RESULTS,
payload: results
})
}
}
// results.js reducer
import {
FILTER_RESULTS
} from '../actions/results';
export default function(state = {}, action) {
switch(action.type) {
case FILTER_RESULTS:
console.log(action.payload); //prints out all the cuts
return {
...state,
filter_results: action.payload
}
default:
return state;
}
return state;
}
const rootReducer = combineReducers({
results: resultsReducer,
});
export default rootReducer;
// bq_results.js component where the FILTER_RESULTS is accessed
render() {
console.log(this.props.filter_results); // only shows the first result
return (<div>...</div>)
}
function mapStateToProps(state) {
console.log(state.results.filter_results); // shows all selected cuts here
return {
filter_results: state.results.filter_results,
}
}
Maybe a better way of putting it is it seems like after the initial state is mapped to props, it is no longer receiving changes to state and mapping it to props.
Came across this article and used Approach #2:
https://medium.freecodecamp.org/handling-state-in-react-four-immutable-approaches-to-consider-d1f5c00249d5
Ended up with:
onCutSelect(cut) {
let cuts = {...this.state.cuts, [cut]: cut}
this.setState({
cuts
}, () => this.props.bqResults(this.state.cuts));
}