I'm working on adding swipe to remove functionality to an app we are developing. For reasons we are not using an external library to handle this, so I am writing it myself.
In my project I have a container where I keep state. I use setState to update the state, and am passing state down to this child component as a prop. In the component below, componentWillReceiveProps is called with the correct value updates when they happen, but the child component of this is not receiving updates to its props. If this doesn't make enough sense or you need to see more code let me know. I've only included the parts of code that I feel are relevant since this is a private project.
constructor(props) {
super(props);
this.renderWishlistRow = this.renderWishlistRow.bind(this);
}
renderWishlistRow(product) {
return (
<WishlistRow
item={product}
onItemPress={this.props.onItemPress}
onRemoveAction={this.props.onRemoveAction}
shouldCloseRemoveButton={this.props.shouldCloseRemoveButton}
onScrollAction={this.props.onScrollAction}
itemPressed={this.props.itemPressed}
onEndScroll={this.props.onEndScroll}
/>
);
}
Then, inside the render function:
return (
<KeyboardAwareListView
dataSource={this.props.dataSource}
renderRow={this.renderWishlistRow}
renderSeparator={renderCardDividerAsSeparator}
onScrollBeginDrag={this.props.onScrollAction}
scrollEventThrottle={16}
onScrollEndDrag={this.props.onEndScroll}
/>
);
Thanks in advance for any help.
EDIT:
I am setting state in the parent component with this code:
this.setState({
shouldCloseRemoveButton: true,
});
I didn't originally include it because componentWillReceiveProps is being called with the correct state changes from the parent component.
EDIT 2:
My App Hierarchy for this part of the app is as follows:
WishlistContainer: contains the setState calls and passes as a prop: shouldCloseRemoveButton={this.state.shouldCloseRemoveButton}
Wishlist: passes props to its child WishlistRow: shouldCloseRemoveButton={this.props.shouldCloseRemoveButton}
WishlistRow: Continues to pass the props down as above, but componentWillReceiveProps is not called here, props are not updating at this level.
I'm not going to mark this as answered, because I want a real answer and what I did to work around this is not good enough for me.
That being said, my workaround was to move the piece of state I was trying to propagate into react-redux. Setting the redux state to contain what I needed using mapDispatchToProps, and then connecting the components that actually needed the state down the line using mapStateToProps, allows the components to receive the notifications they need to do their thing.
Again, I am not choosing this as the answer - even though it is what I did to solve the problem - because something else fishy is going on somewhere and I would like to see if anyone knows why things didn't work as they were.
EDIT
I've run into this issue other times since this originally happened. There is a prop that exists on the Flatlist - This is not the original component I used in the question, but the original component is deprecated now and the Flatlist has the, about to be mentioned, prop for this scenario - called extraData. This particular prop is also watched to help the Flatlist determine if it should rerender itself.
Since the ListView has become deprecated, I feel that using a Flatlist and making sure you pass in an extraData prop - assuming you have a different prop that will change with your list data changes - is an acceptable answer to this problem.
Related
I want to know the concept of the below thing-
I have created one component and set up its respected event listeners. Now, I want to remove those listeners on this component's beforeDestroy hook before redirecting to another route that will create another component.
but what I noticed is, beforeDestory hook of the first component is calling even after the second component's created hook.
I want to destroy the first component completely and then create another component.
// To set up the event listeners
created() {
this.EventBus.$on('myCustomEvent', payload => {
// some code here
)}
}
// To destroy the event listeners
beforeDestroy() {
this.EventBus.$off('myCustomEvent');
}
Any suggestions?
In search of an answer to your question, I came to the conclusion that it is better to refuse to use EventBus altogether.
Here is some information on this and some thoughts from there:
I have that feeling that having an EventBus in Vue is an anti-pattern, especially if you’re using VueX but I can’t quite put my finger on it. At the point you want to be sharing data like that, wouldn’t it be better to use a store to handle all of those “events” / “mutations”?
Also, looking at the solution to this issue, you are doing everything right and there is no other way.
I'm using code like this it is not working for me
componentWillRecieveProps(nextProps,nextState){
if(nextProps.navigation.state != this.state){
//here calling api
}
}
I think the question should be more elaborative to help to solve your issue. Based on your questions, you may be asking for either -
How to reload page if data changes
How to reload page if navigation state changes
How to reload page if data changes
In this case, you no need to add 'componentWillRecieveProps'. You can simply check for a value in render method like this
{data.items.length && <ItemLIstCustomComponent />}
or
{data.items.length ? <ItemLIstCustomComponent /> : <EmptyMessageComponent />}
You can look into conditional rendering in react-native.
If the re-render is not happening still? Then your way of updating state/data is not correct. Make sure you make copy of object and update the existing one.
To understand this, read about mutable objects and react shallow-check for re-rendering. Or may be this will help
How to reload page if navigation state changes
I don't think it is good practice to call another API or do any state change related thing in componentWillRecieveProps. You can create a new Screen on which you navigate and call API in componentDidMount.
You can try React navigation if you are not using any navigation library.
I'm trying to show an activity indicator, when I go from one page to another. The target page contains many components within it, and it takes time to load. that's why I need some way to listen when all the child components are loaded, and at that moment tell my variable isBussy to be false
<template>
<StackLayout>
<ActivityIndicator :busy="isBussy" v-if="isBussy" />
<StackLayout v-else>
<Component1 />
<Component2 />
<Component3 />
<Component4 />
</StackLayout>
<StackLayout>
</template>
<script>
import Component1 from '~/components/Component1'
import Component2 from '~/components/Component2'
import Component3 from '~/components/Component3'
import Component4 from '~/components/Component4'
export default {
data() {
return {
isBussy: true
}
},
mounted() {
this.$nextTick(function() {
// Code that will run only after the
// entire view has been re-rendered
this.isBussy = false
})
}
}
</script>
this code does not work, since once the navigation is indicated from the previous page with:
#tap="$goto('otherPage', { props: { foo: bar } })"
it remains stuck on the initial page, and all the components begin to load in the background of the destination page, but without displaying the parent page, changing to this, only when the whole process ends, and never show/hide the activity indicator as expected.
By the way this expected behavior works perfectly when i do request and process them with Promises, then I turn on or off a variable in the state and it works. but I can not replicate that behavior in the navigation between pages and listen to load all the components
EDIT
Finally I achieved the desired behavior with a little trick I found on the internet
mounted() {
setTimeout(() => {
this.isBussy = false
}, 500)
},
this causes that the rendering of all the children components is delayed only a little, so that the activity indicator is shown, but not too much to produce that none of the components contained in the else block is detected and begin to rendering
There are two main ideas to understand here I think. I'll describe both.
1. General technique to Fetch Data without blocking render
It sounds like you understand this concept at the parent component level but then are asking how to do something very similar for the child components that this page contains.
The way I handle this, is in my component, I have my data default to an isLoading state. Then, in beforeMount() or mounted(), I perform my asynchronous actions and make necessary changes to my page's data.
The problem becomes entirely recursive when we look at child components. You want to make sure your child components are rendering and that any long running data fetching that needs to occur within their implementation will simply cause them to re-render once that fetching is complete.
Here is a working example: https://codesandbox.io/embed/r4o56o3olp
This example uses Nuxt. Aside from the addition fetch() and asyncData() methods, the rest of the Vue lifecycle hooks are the same here.
I use new Promise and setTimeout to demonstrate an operation that would use promises and be asynchronous. (e.g. axios.get(..))
The About page loads, and the beforeMount() lifecycle hook performs the asynchronous fetching in a way that doesn't block the page from rendering.
I use the beforeMount() hook because, according to here ( https://alligator.io/vuejs/component-lifecycle/ ), it is the first lifecycle hook that we have access to once the page's data is reactive. (So modifying this.myDataProp would trigger a re-render if {{ myDataProp }} was used in the template).
I also included a child component where I purposely made its data take twice as long to load. Since I again, am letting the component render immediately, and then I handle the fetching/updating of data in an appropriate lifecycle hook, I can manage when the end-user perceives a page to be loaded.
In my working example, the LongLoadingComponent did the same exact technique as the About page.
Once you see how to use beforeMount() or mounted() to fetch data and then update state, I think the trick is to take a moment and really think about the default state of your component. When it first renders, what should the user see before any of it's data fetching/long-running operations are completed?
Once you determine what your default (not yet loaded) component should look like, try getting that to render on your screen, and secondarily add in the logic that fetches and updates state data.
2. Listening for when a Child Component is finished rendering from a parent component
This makes use of the above technique, but includes the usage of the updated() hook and emitting a custom event ( https://v2.vuejs.org/v2/guide/components-custom-events.html
)
If you really want to listen for when your child components are finished rendering, you can $emit a custom event in your updated() hook. Perhaps something like this (in one of your child components)
if (this.dataLoaded) { this.$emit('loadedAndRendered') }
So when the child's async operations are done, it can flip it's dataLoaded property to true. If dataLoaded is used in the child's <template> somewhere, then the component should re-render (for it's "finished" state). When the child re-renders, the updated() hook should trigger. (again, see: https://alligator.io/vuejs/component-lifecycle/ ) I included the if (this.dataLoaded) part just to handle case where updated() hook might be called during intermediate data updates. (We only want to emit loadedAndRendered event if child is finished loading data/updating.)
3. Other caveats about universal nuxt applications
It wasn't until after I wrote this answer that I realized you aren't using Nuxt. However I'm adding this in case other Nuxt users happen to come across this.
I'm adding this section just because it took some focused hands-on time for me to wrap my head around. A Nuxt Universal Application does both server-side and client-side rendering. Understanding when something renders on the client vs when it was rendered on the server was a little difficult for me at first. In the working example I linked above, when you visit the about page you can also see if that component was fetched from the server or if it was just rendered by the client.
I'd recommend playing with a Page's fetch() and asyncData() methods and see how it impacts when certain things render on your screen. ( https://nuxtjs.org/api/pages-fetch/ ) ( https://nuxtjs.org/api/ ). Seeing what these methods are useful for helps me also identify what they are not useful for.
If you're using a Vuex store, I'd recommend seeing what happens when you refresh a page or use instead of a to navigate between pages. (Seeing something like the SSR schema diagram can be helpful here: https://nuxtjs.org/guide#schema )
..I have yet to fully appreciate the details of the bundling and delivery behavior that Webpack provides for a Universal Nuxt app (See right side of diagram here: https://medium.freecodecamp.org/universal-application-code-structure-in-nuxt-js-4cd014cc0baa )
Can You please help to decide which one of the architectures will be better
(fetching list from API with with react-native, react-redux, redux-thunk)
example
// component
componentDidMount() {
this.props.dispatch(fetchFunction());
}
// thunk action
fetchFunction () {
dispatch START_LOADING
return dispatch (
fetch()
dispatch SUCCESS_LOADING
);
}
OR
example
// component
componentDidMount() {
this.setState({'loading': true})
this.props.dispatch(fetchFunction()).finally(
this.setState({'loading': false})
);
}
//thunk action
fetchFunction () {
return dispatch (
fetch()
dispatch SUCCESS_LOADING
);
}
My idea is about storing "loading process" in local components state? What are bad and good sides of this approaches?
as i see it - example 2:
If loading takes longer time, i can leave component (it gets unmounted) and i will see warning - "changing state on unmounted component"
1 example:
Saves a lot of extra data that i do not need in redux store (also a lot of data i need to exclude from persist), and for example if i have a web store product component, i will have a lot of UI props stored in redux (for example list with 10 products, and each product has it's own buttons with states) :(
BUT i like it because all the logic stays in redux-thunk-actions, and i do not need to care in component how to continue coding after dispatch, whether it was a promise or wasn't example:
this.props.dispatch(fetchFunction()).then(() => {});
or just
this.props.dispatch(fetchFunction());
.. some code
So far I've made simple projects, and both ways worked fine.
Can you please give some advice which way to go for the bigger project?
If components outside of this component's tree need to know about the loading state, then keep the loading state in the store so that those outside components can query that state.
In a large app, whether a components need to know of a given piece of state is something can changes; starting out, Component X has local state because no other components need to know about local state X, but as the app grows, new components, thunks, etc are introduced that do need to know about local state X, and it has to be moved to the store. So to address this, one approach is to put all state in Redux from the beginning so you don't have to worry about unforeseen refactoring of local to global state.
Keeping everything in the redux store also makes debugging easier because you can see all the values in redux devtools rather than having to find individual components to see their local state.
I am new to react native and I am having a warning that I just don't know how to get rid of it, is it ok if I just hide it as long as it does not become an error to crush my app?
The warning I receive:
Warning: Cannot update during an existing state transition (such as
within render or another component's constructor). Render methods
should be a pure function of props and state; constructor side-effects
are an anti-pattern, but can be moved to componentWillMount.
I tried to manage to not happen this warning but if I register a user and than go back to signupscreen and if I try to register another one the warning comes up.
It's best to resolve that warning, you need to look where your updates to the state are happening.
If you do updates to the state or props inside of your render function you are creating infinite render loops.
To help with this problem I need to have your code from render() function. You can Upload it here. Then I can review your code. It will help to solve this problem.