UseEffect not triggering when used with MaterialTopTabNavigator - reactjs

This is my first time using this platform to ask questions so please pardon me if my question does not seem well developed.
brief introduction
what I am trying to achieve is a dynamic Tab navigator, whereby the number of tabs changes depending on the number of elements in an array where this array changes in the number of elements over time, i.e :
{
userIds : [1,2,3,4,5,6]
}
will render a tab navigator with 6 tabs
I am using react-redux for managing state and I have been following this tutorial on youtube just for your information: https://www.youtube.com/watch?v=9boMnm5X9ak&list=PLC3y8-rFHvwheJHvseC3I0HuYI2f46oAK
context
in the main code snippet the action FetchMonthlyTransIdAct() is being dispatched, this consist of 2 actions being dispatched in order :
RequestMonthlyTransaction → FetchSuccess or FetchFail
(as per mentioned in FetchMonthlyTransIdAct.js, ) the initial state is as follows and the changes each action does :
{
loading : false
Id : []
error : ''
}
{
loading : true //when RequestMonthlyTransaction is dispatched
Id : []
error : ''
}
{
loading : false // When FetchSuccess is dispatched after RequestMonthlyTransaction
Id : [1,2,3,4,5,6]// When FetchSuccess is dispacthed after RequestMonthlyTransaction
error : ''
}
{
loading : false //when FetchFail is dispacthed after RequestMonthlyTransaction
Id : []
error : 'some error message here' //when FetchFail is dispatched after RequestMonthlyTransaction
}
problem
so the problem that I am currently facing is that useEffect does not seem to trigger when I am rendering components with navigationContainer/ tab.navigator
here is the snippet of my code, I have narrowed down the source of the problem between asterisks
const Tab = createMaterialTopTabNavigator();
const mapStateToProps = state => {
return {
userData: state.MonthlyEntry
}
}
const mapDispatchToProps = dispatch => {
return {
FetchMonthlyTransId: () => dispatch(FetchMonthlyTransIdAct())
}
}
const EntryTabNavigator = ({userData, FetchMonthlyTransId}) => {
useEffect (() => {
FetchMonthlyTransId()
}, [])
console.log(userData.Id)
if (userData.loading || userData.error != '') {
return <View/>
} else {
return(
**************************************************************************************
<NavigationContainer independent = {true}>
<Tab.Navigator swipeEnabled = {true} tabBarOptions = {{scrollEnabled:true, tabStyle:{width:120}}}>
{userData.Id.map((data) => {return (<Tab.Screen key = {data.toString()} name = {data.toString()} component = {MonthlyTransactions} initialParams={{id:data.toString()}}/>)})}
</Tab.Navigator>
</NavigationContainer>
**************************************************************************************
)
}
};
export default connect(mapStateToProps, mapDispatchToProps)(EntryTabNaviga
the error message simply that there was no screen for tab navigator to render (due to userData.Id being an empty array when it should not)
based on the console.log(userData.Id)
the expected output should be Array [1,2,3,4,5,6]
but the actual output was Array [] which indicates that the useEffect was not triggered
I have tried replacing the snippet of code between the astericks with
<View><Text>{userData.Id}</Text><View> and it was able to render as expected (returning a screen with the string representation of the array as the text), hence leading me to identify that the code snippet between the astericks is the problematic portion. I have also tried adding a console.log statement within useEffect and it does not output anything into the console when I have the code snippet in asterisks, however it does output into the console when I replaced the snippet of code between the astericks with <View><Text>{userData.Id}</Text><View>
should there be a similar problem to this that has already been asnwered, it would be much apppreciated if you could direct me to it, it would also be great if you could point me to resources to improve my knowledge with redux (prefreably beginner friendly) ! additional reference code (reducer and action) is below
Thank you in advance
FetchMonthlyTransIdAct.js
const requestMonthlyTransaction = () => {
return {
type: "REQUEST_MONTHLY_TRANSACTION",
}
}
const fetchSucess = (ids) => {
return {
type: "FETCH_SUCCESS",
payload: ids,
}
}
const fetchFail = (error) => {
return {
type: "FETCH_FAILURE",
payload: error,
}
}
export const FetchMonthlyTransIdAct = () => {
return (dispatch) => {
dispatch(requestMonthlyTransaction())
async function getId() {
return require('../../data/DummyId.js').id //returns [1,2,3,4,5,6]
}
getId().then(
id => dispatch(fetchSucess(id))
).catch(
error => dispatch(fetchFail(error))
)
}
}
FetchMonthlyTransIdRed.js
const initialState = {
loading:false,
Id : [],
error:''
}
const FetchMonthlyTransactionIdRed = (state = initialState, action) => {
switch (action.type){
case "REQUEST_MONTHLY_TRANSACTION":
return {
...state,
loading: true,
}
case "FETCH_SUCCESS":
return {
...state,
loading: false,
Id: action.payload
}
case "FETCH_FAILURE":
return {
...state,
loading: false,
error: action.payload
}
default: return state;
}
}
export default FetchMonthlyTransactionIdRed;

after much tinkering, I manage to find a solution (or a workaround rather) to the problem. which is to add an initial element in the array of the Id attribute in the initial state in FetchMonthlyTransIdRed.js, that will allow the first render of the navigation component to occur without issues, and subsequently in the next re-render when FetchMonthlyTransId is dispatched Id is then updated with the array that I have imported

Use React Navigation's useFocusEffect, e.g.:
import { useFocusEffect } from '#react-navigation/native';
function Profile({ userId }) {
const [user, setUser] = React.useState(null);
useFocusEffect(
React.useCallback(() => {
const unsubscribe = API.subscribe(userId, user => setUser(user));
return () => unsubscribe();
}, [userId])
);
return <ProfileContent user={user} />;
}

Related

Why reducer with array.sort doesn't trigger re-render?

I've got a problem with re-rendering table in React.
So at first is how it looks like
When I fetch data from SWAPI (I'm doing it in action) and passing it into reducer, it works fine and table component re-renders without any trouble.
But when I'm trying to sort it via SORT_RESULTS reducer, it just doesn't do anything with table itself. It actually modifies state as FETCH_DATA reducer do, but doesn't do anything, however I can log sorted information and make sure it was sorted as intended.
Thanks in advance for your help!
// actions.ts
export const onDataFetched = (currentPage: number = 1) => (
async (dispatch:any) => {
let data: {} = await fetch(`http://swapi.dev/api/people/?page=${currentPage}`)
.then(response => response.json())
const dataToReturn = {
type: 'FETCH_DATA',
payload: data
}
dispatch(dataToReturn)
}
)
// reducers.ts
case FETCH_DATA:
// returns new state object
return {
...state,
swapi_data: {
...action.payload
}
}
// actions.ts
export const onSortResults = (payload: string) => (
(dispatch:any) => {
dispatch({type: 'SORT_RESULTS', payload})
}
)
// reducers.ts
case SORT_RESULTS:
let results = state.swapi_data.results;
// title is heading of a table column (e.g. 'name', 'weight', etc)
const title = action.payload;
// sorts array with persons (objects)
results.sort((a: any, b: any) => {
return (a[title] > b[title]) ? 1 : ((b[title] > a[title]) ? -1 : 0)
})
// as in example with FETCH_DATA reducers it's doing the same job - returns new state object
return {
...state,
swapi_data: {
...state.swapi_data,
results: results,
},
}
Array Sort Mutates
If you mutate there is no change detected since the reference is the same. But sort also returns itself so you can add this code. The spread operator makes a copy and there for changes the reference meaning that your changes will be detected.
const sortedResults = results.sort((a: any, b: any) => {
return (a[title] > b[title]) ? 1 : ((b[title] > a[title]) ? -1 : 0)
})
return {
...state,
swapi_data: {
...state.swapi_data,
results: [...sortedResults],
},
}

Redux , state.concat is not a function at rootReducer. And being forced to reRender an element for it to see the state change

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 :)

My redux thunk Axios calls return empty objects in my Redux store

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]

component mounting before fetch is complete, resulting in failed propTypes

I have a tour website. The basic structure here is that I have the tour page, and on the tour page is a component that handles booking times. When mounted, TourPage fetches the available start times from the db and ships that data down as props.
I am able to receive this data and send it down, but the deeper component gives back a propTypes error, saying the data is undefined. When I console log out (from parent) the value of this data, it logs 3 times: first, it is an empty array. The second and third time, the data is there.
Odd thing is that the propType error appears chronologically AFTER my console.log showing the actual data. I have also added logic to the TourPage to return a Spinner if the availableStartTimes array is empty. I fully expect that no children components will be mounted until all data has arrived. This does not seem to be the case.
Here is some relevant code (edited for brevity):
const ExperienceDetailContainer = () => {
//a function that cleans the fetched data. returns an array
const availableStartTimes = getTimesForCurrentDate(experienceDate, detail.availabilities)
//fetch request to get available start times
const getExperienceAvailabilities = () => {
api.post('/GetExperienceAvailabilities', { experienceid: experienceId })
.then(result => {
const availabilities = result.d
updateExperience(availabilities)
})
.catch(err => {
console.error(err)
})
}
useEffect(() => {
getExperienceAvailabilities()
}, [])
if (availableStartTimes.length < 1) {
return <Spinner />
}
return <ExperienceDetail {...{ availableStartTimes, experienceDate }} />
const mapStateToProps = state => ({
detail: fromResource.getDetail(state, 'experience'),
experienceDate: state.userSelections.experienceDate,
})
const mapDispatchToProps = (dispatch) => ({
readDetail: (apiRequestParams) => dispatch(resourceDetailReadRequest('experience', { apiRequestParams })),
updateExperience: (availabilities) => dispatch(updateExperience('experience', availabilities))
})
here is getTimesForCurrentDate helper function:
export const getTimesForCurrentDate = (date, availabilities) => {
if (!availabilities) {
return []
}
const rawTimes = availabilities[formattedDateWithoutTime(date)]
const properTimes = rawTimes.map(rawTime => {
return convertTimeFromRawToProper(rawTime)
})
return properTimes
}
here is the propTypes for BookCard component
BookCard.propTypes = {
availableStartTimes: T.arrayOf(T.string).isRequired,
}
and the reducer for updateExperience
case UPDATE_EXPERIENCE:
return {
...state,
experience: {
...getResourceState(state, resource),
detail: {
...getDetail(state, resource),
availabilities: payload
}
}
}
here is a screenshot of my console to demonstrate the 3 outputs of availableStartTimes, as well as a console.log(availableStartTimes.toString()) placed after the Spinner guard if statement

my state get filled and get empty again ? React-Redux

Hello I am having a little problem here, I am trying to render data in my component, here is my component :
// Store data in the global state
const mapStateToProps = ({ moduleState }) => ({
operationsContrat: moduleState.contrat.operationsContrat
});
// execute the action
const mapDispatchToProps = dispatch => {
return {
getOperationContrat: (...args) =>
dispatch(getOperationContrat(...args))
};
};
class Operations extends Component {
state = {
data: []
};
// Nod used
componentDidMount() {
if (this.props.operationsContrat.data.length === 0) {
this.props.getOperationContrat(4000003);
}
}
componentWillReceiveProps(nextProps) {
if (this.props.operationsContrat.loading && !nextProps.operationsContrat.loading) {
this.setState({ data: nextProps.operationsContrat.data });
}
}
render() {
console.log("operations");
console.log(this.state.data);
return (
<Fragment />
);
}
}
So I get the data and then my state.data is empty again, whch caused me a problem, because No data is displayed, here is what I see in my console :
So what happens is that my componentWillReceiveComponent is not executed when my render method is executed the last time, I know what happened I guess but I dont know how to solve the problem..
I can provide any needed code if you need it to understand more the problem.
Any help would be much appreciated.

Resources