-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
setState doesn't trigger rerender #134
Conversation
// we split the setup of the state and the react component, since calling setState | ||
// in render is not allowed. | ||
|
||
const splitIndex = compiledCode.indexOf('React.createElement'); |
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.
Will it work with ES6 classes and functional components?
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.
In my examples does it work. The React.createElement () is just the result after the JSX transformation. So <Button />
becomes React.createElement(button,...)
Thank you very much, this is very cool! 'count' in state || setState({ count : 1}) It was a hack so I won’t miss it ;-) <Message content={require('./mocks').hello}/> It’s fine when you need it only once (I think in most of the cases). Any way to reuse the required module? |
I don't like this implicit separation of code into |
@mik01aj I agree but it fixes more things than breaks. |
For me it will break many examples, I use mocks pretty often. For example:
|
Maybe we could “hoist” |
Let's think again. We're talking about example usages of a component. If, in order to use a component, you need some special initialization in So maybe all we need is just some way to set the initial state, and then everything else could be done in So I would propose this:
We could then run this once to get the initial state, and then just put this in |
Actually I like this idea very much ;-) |
We should probably also consider the case that somebody requires a module and uses this to initiate the state and his component as well. For this we need to make some assumptions / heuristics about the example codes. I will give it a try in the next days :) |
@saschatimme I think it will also work this way. That's what I think:
|
I got it running with the idea of @mik01aj, thanks! I also added a error if the old syntax is used, since the setState would now cause a stack overflow. Maybe you could could give this version a try with your examples and let me know if I overlooked something :) |
I need to try it one day. |
Any update on this? |
@tizmagik Sorry didn’t have time to check it. |
I tested it. It seems to fix the animations, but on the other hand, it breaks examples like this:
They fail with this error:
I'm OK with this (I mean, I can update my examples so they don't create components), but this is a breaking change. See also comments in the code. My biggest concern is this code splitting, it is a bad, bad idea. It will break examples like this:
|
if (this.state.error) { | ||
return <pre className={s.playgroundError}>{this.state.error}</pre>; | ||
} | ||
const setState = partialState => this.setState(partialState); |
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.
setState
in React is defined as void setState(function|object nextState, [function callback])
; you missed the 2nd argument.
Then we can merge it and combine with hiding code examples by default in a single major release. |
// the code contains the setup of the state and the react component to render. | ||
// we split the setup of the state and the react component; | ||
|
||
const splitIndex = compiledCode.indexOf('React.createElement'); |
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 a bad idea. And after the discussion in the PR, I thought we don't need any splitting anymore.
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.
Yeah, I also thought that we’ve found a better solution.
const renderCode = ` | ||
var initialState = {}; | ||
${compiledCode.substring(0, splitIndex)} | ||
return ${compiledCode.substring(splitIndex)}; | ||
`; |
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.
In order to pass the setState Function and the state into our component, we have to use new Function
and cannot work with eval
(this happens ins this.props.evalInContext
). The current implementation uses the fact, that eval
returns the result of the last statement, but for a with new Function
generated function this is not the case. Hence to get a proper render Function with a return value, we have to insert somehow a return statement for the last component. This is the only reason splitIndex
exists.
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.
Why not just add more arguments to evalInContext
instead?
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 do you mean? I don't see what extra argument could solve the problem.
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.
In examples.loader
, there was this line:
'function(code, setState) {' +
The setState
callback was passed to the component and it worked fine. If you needed to pass something more, you could just add arguments to this function. Why did you change it to use new Function()
? Without that change, things would get simpler here.
Would be very nice merge this PR. @saschatimme are you going to finish it? |
Sorry I was quite busy the last two weeks but I will try to find a solution that doesn't involve any code splitting this weekend. |
@saschatimme Cooool! |
@sapegin I removed the code splitting and the counter examples from mik01aj are now working |
code = ` | ||
const state = Object.freeze(${JSON.stringify(this.componentState)}); | ||
${code} | ||
let compiledCode = this.compileCode(this.props.code); |
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.
const
?
@saschatimme Could you please update your branch and fix conflicts? I’ll merge it after that. |
There are a few ESLint warning now. |
@sapegin sorry for that, it should be fixed now. |
Thanks! |
This pull request should solve the issues in #51.
The previus method was basically, that we keep the state in memory and at every call of setState we render everything again. The problem is that this breaks animations as well as other "dom-dependent"actions.
This would have the following breaking API changes:
Instead of:
it would just be:
Another breaking change would be, that instead of:
you would have to call require directly in the component:
The reason is that I split the example code at the (single) react component. Everything before will be executed in the
componentWillMount
and the rest in therender
method of a react component (calling setState in a render method is not possible).Would love to hear some feedback.
P.S.: The examples seem to be broken because of the "dog-names" packages, which runs into an error.