-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
removing componentWillReceiveProps #222
base: develop
Are you sure you want to change the base?
removing componentWillReceiveProps #222
Conversation
This pull request is automatically deployed with Now. Latest deployment for this branch: https://websiteone-fe-git-fork-jmpainter-165-replace-componentw-4c766c.agileventures1.now.sh |
hey @jmpainter, sorry I haven't responded to you earlier, I saw this, but had a short day yesterday and was trying to get another We have an issue to do this, which you have linked to so I guess you have seen. Thanks for getting this going for us! I guess your doubt is about using I think most of the cases we are using In that case, using |
Hi, no problem - I figured this was not a pressing issue but it is an interesting one so I thought I'd work on it. What I'm seeing is that in the case of ProjectList and UserList, componentDidUpdate can be used to replace what is currently in componentWillReceiveProps. These cases involve state changes that are done in response to a successful update from the api. In all the other cases, I think componentWillReceiveProps is not needed at all. The contained component can check for complete props from the parent's update. Everything looks ok with this solution locally. If this strategy looks ok, I can change the tests. |
it looks ok to me, of course there is always the possibility someone else on the team will disagree, but I'd say if they haven't made their opinion known till now, maybe just go with it, and if @joaopapereira agrees as well, it seems like a definite improvement! |
Ok, great - I'm going to fix the tests. |
@@ -11,7 +11,7 @@ describe('Event Info', () => { | |||
slug: 'weekendcollaboration' | |||
}, | |||
fetchEventInfo: jest.fn(), | |||
setLastLocation: () => {}, | |||
setLastLocation: () => { }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is motivating all of these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setLastLocation
should return a string, not an object
it was added to redirect the user back to the page they were on after successfully logging in
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mattwr18 Didn't know that. That seems like an issue with pre-existing code.
What I meant is, what is motivating making all the empty object literals fat -- { }
. It doesn't appear to be a linting rule, and there are no fat object literals in the codebase prior to this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this is some local linter rule that should never get to this diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple things
Is there a compelling reason for switching to componentDidUpdate
rather than getDerivedStateFromProps
?
Both of the replacement componentDidUpdate
implementations call setState
one or more times under the hood, which should lead to multiple renders.
You axed some tests. Can you explain how the current state of the test suite is still checking the same behavior, or how the checks you eliminated were unnecessary?
If you additionally don't see rendering from props currently being or previously having been tested in a way that actually mimics app flow for the relevant stateful components, when do you think this should be addressed?
I'm really not sure why we are making our empty object literals fat -- { }
.
Perhaps there is a compelling reason I'm unaware of, and if so I'd be happy to learn what it is. If not, unnecessary style changes can breed inconsistency and conflict, and they certainly breed noise.
I'm not sure if this is a compelling reason @Sherspock, but I found this article @jmpainter I'll have to pull this down and interact with it, I think there might be code removed here that was specifically added to deal with edge cases you might not have run into when manually testing it, though I could easily be mistaken |
I also had looked at the article 'You probably don't need derived state": |
if (this.props.project.slug === this.props.match.params.slug) { | ||
this.setState({ project: this.props.project }) | ||
} else { | ||
if (this.props.project.slug !== this.props.match.params.slug) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is much better logic @jmpainter... great job!
I did notice a small interval where clicking on a different project shows the previous project's info until the store is updated with the current project's info from the fetchProjectInfo
action, but I used throttling to test it with regular 2g and it was noticeable, but probably not an issue.
return ( | ||
<Container className='event-info-container'> | ||
<EventSummary event={event} /> | ||
<EventSummary event={this.props.event} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is great! maybe I was overthinking things with setting the state instead of just using what is already in the store
@@ -125,6 +118,7 @@ export class CreateEventPage extends Component { | |||
name={name} | |||
category={category} | |||
eventFor={eventFor} | |||
projects={this.props.projects} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch, not sure how I didn't catch this before.
I noticed it this weekend, but since you have already put in the fix, it saves me time 😸
@jmpainter @mattwr18 @mattwr18 |
@mattwr18
componentDidUpdate () {
if (this.props.projects.length > 0 && this.state.languages.length === 0) {
this.paginateProjects(this.props.projects)
}
}
componentDidUpdate () {
if (this.props.users.length > 0 && this.state.usersList && this.state.usersList.length === 0) {
this.normalizeUsers(this.props.users)
}
} First of all, I don't love that we are keying off of both state and props in these methods. I don't see why we wouldn't be fully in the state world at this point. Second of all, we want projects to only paginate if languages is empty? And same for users? Third of all, what happened to the behavior where we don't paginate if the first project or user in the collection has The fact that the build is green despite apparent second and third issues suggests that either:
|
I'd like to add my own review here. From my perspective, this new approach absolutely is worse than what was there before. It doesn't seem quite right that ComponentDidUpdate would be the place to watch for the new props coming in through redux after the api call in ComponentDidMount. Furthermore, there is a real risk in creating an infinite loop caused by the state changes called for in ComponentDidUpdate. That's what all the conditional checks are about. This pr was an attempt to replace componentWillRecieveProps with something else (or nothing). However, I don't claim experience or knowledge of how to fix this issue. This approach looks like it works, but that doesn't mean it's great. I'm attempting to learn as a developer by taking on issues that will be a challenge and getting constructive feedback that will help me improve. @Sherspock , do you have any suggestions for changes or improvements? |
@jmpainter That is a super badass attitude. I have some thoughts on implementation but I don't claim to be a React/Redux expert or even journeyman, so they may not serve much use beyond a conversation starter. With that caveat I'll post them this weekend. @mattwr18 Also very happy to hear your thoughts in reply I do think it's good to mention that -- as I understand it -- this PR was a proactive and very welcome experimental attempt to replace As I fallibly understand it, given that this work was done without a motivating issue -- and was supported by the brave and potentially very fruitful "I dunno, let's see" approach -- we're not set greatly set back if we scrap it and go in a different direction. I'm not yet saying let's do that, I'm just saying let's not be afraid to do that if warranted. I will go ahead and ask the general question: And the specific questions: |
I also intend to defer on this PR (barring major objections) to @mattwr18 and other WebsiteOne regulars like @joaopapereira |
Thanks for the remarks. A draft PR would have been good for this. I hadn't heard of it before now but it sounds like a good way to present code for discussion without implying it should be merged at that point or ever. After giving the situation with these containers some thought, I think what is going on here is that redux store state is being duplicated in local state or normalized and stored in component state. While it is not necessary to have all state in redux, It may be an anti-pattern to have a combination of redux store and local state for the same data or data that is derived from that data. From this article https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html Common Bugs When Using Derived State: What is going on in the ProjectsList.js and UsersList.js containers in componentWillReceiveProps amounts to data normalization that could be done as part of the redux flow through actions and reducers and stored in the redux store. Then redux could do its job and provide this to the containers as props. In the case of a container like EventInfo.js, the local state copy of what is in the redux store can be eliminated because it will be provided correctly by redux once the api call returns. Then componentWillReceiveProps can be eliminated without introducing issues with replacing it with componentDidUpdate. |
My fallible thoughts on the subject: Seems to me like what is warranted is extracting the useful bits of this PR that do not really relate to removing I would then close this PR. Personally, if I were a regular on this project, I would also insist that the fat empty object literal change not be part of this or any subsequent PR without reasonable justification. Some more thoughts, understanding that I will defer to decisions made by the team in this neck of the woods: I question the general approach we're using for both pagination and state management for the It also seems odd that the components seem to only ever care about paginated data to display, yet we're letting them get initialized with data that isn't paginated. If they really only care about paginated data, why wouldn't we move the pagination logic into the corresponding reducers? I suspect we could get rid of a lot of needless state management and wondering where things should go by off-loading the pagination responsibility completely and hooking into a pagination library. Things seem tangly here, but I think that if we're going to take the time to bring these components into compliance with preferred lifecycle methods, we should also take the time to think about and discuss the right way to do it. An alternative would be to perform this conversion in a more disciplined way and ensure that the logic in the new lifecycle methods is functionally identical from a user's perspective. |
Oh my gosh I feel like we just had a cosmic moment with the "put it in the reducer" intersection. Yes, no single source of truth for state isn't great. I like where you're thinking and may have overcomplicated or gone a bit too architecty in my response. Curious to also hear thoughts from others |
I know there have been quite a few topics to give opinions on here @jmpainter @Sherspock @joaopapereira... sorry I haven't been able to write anything till now. You bring up a lot of good points @Sherspock, and I appreciate that. There are definitely many things that can be improved/questioned in the code base. Several of them @joaopapereira has brought up. Some of the logic that doesn't seem coherent @Sherspock was decided on the fly, by a group of developers, some of which are not actively taking part in the process at the moment, not uncommon. At the moment, I am mostly focused on trying to get things checked off blocking us from going to production. Is anything you mentioned ☝️ something that would block that, or could it be dealt with in parallel, or even after going to production? There is also work to be done to ensure there are issues for people to work on, I have several developers getting in contact with me wanting to know what to do. I feel like it would be nice to come up with an answer to this potential issue, but maybe it doesn't take the highest priority, but happy to discuss it if others disagree. I would encourage people to come to the meetings, if at all possible, Tuesdays |
I think that componentWillReceiveProps can be removed from all the containers that include it. I don't think that it is necessary to mirror state in the store with local state in the containers and then make a check for updated props in componentWillReceiveProps. In some cases, where a check is needed, componentDidUpdate can be used instead. A lot of tests are broken here, I'm waiting to see if this approach seems reasonable.
src/components/EventSummary.js - check for empty event added
src/components/UserSummary.js - check for empty user added
src/containers/CreateEventPage.js - projects removed from state and this.props.projects added as prop to EventForm - event removed from state, using state from redux store instead
src/containers/EventInfo.js - event removed from state
src/containers/ProjectInfo.js - project removed from state
src/containers/ProjectsList.js - componentDidUpdate used instead of componentWillReceiveProps
src/containers/UserProfile.js - user removed from state, using user from redux store instead
src/containers/UsersList.js - componentDidUpdate used instead of componentWillReceiveProps
fixes #165