I have the following component...
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
register: false
}
}
handleClick(event)
{
console.log("link was clicked");
this.setState({register: true});
}
render(){
return (
<If condition={ this.state.register }>
<Then><Register /></Then>
<Else>{() =>
<Index event={this.handleClick} />
}</Else>
</If>
);
}
}
module.exports = App;
So my function handleClick is called whenever a link is clicked, this changes the state to true. However, the <IF> statement does not notice that state changed and so remains the same (only Index remains)
What I want is for <Register /> to be rendered whenever the link is clicked. So if the link is clicked, the state will change to true, and then the IF statement will catch this change and render the right component.
How can I do this? How can I get the IF statement to notice the change in state?
Not sure what <Index> is or what is it doing with event. If it just calls the function that is passed as props.event, the handleClick function is called with the wrong this reference and doesn't set the state for the correct component.
If you're using Babel with stage-2 preset or lower, you can use syntax to bind this:
class App ... {
...
handleClick = (event) => { ... }
...
}
Yes, it's an arrow function definiton, but directly inside class. It will bind the correct this reference so it can be passed around and it will have proper this reference when called.
Alternatively, you can use bind directly when passing the function, as event={this.handleClick.bind(this)}. But beware, this creates a new function every time it's called, so it might impact performance if it's called very often. It's advised that you do not use this approach and airbnb's eslint config will mark this as an error. A better approach would be to do one time binding inside the constructor, as this.handleClick= this.handleClick.bind(this).
If you wish to know more, this Medium post covers 2 more ways to bind and explains all 5 options.
you can do it by following code,
return (
{ this.state.register ? <Register /> : <Index event={this.handleClick.bind(this)} /> }
);
Related
I have the following situation
export default class MyComponent extends Component {
myFunc = dynamicKey => {
// do something with the dynamic key
}
render() {
return (
<Foo>
<button onClick={e => this.myFunc(someDynamicKey1)} />
<button onClick={e => this.myFunc(someDynamicKey2)} />
<button onClick={e => this.myFunc(someDynamicKey3)} />
{/* ... */}
</Foo>
)
}
}
Which is a very common case, but It isn't good because on every render it's creating that arrow function.
So as a walkaround, I made a function that returns another function with that key.
export default class MyComponent extends Component {
myFunc = dynamicKey => e => {
// do something with the dynamic key
}
render() {
return (
<Foo>
<button onClick={this.myFunc(someDynamicKey1)} />
<button onClick={this.myFunc(someDynamicKey2)} />
<button onClick={this.myFunc(someDynamicKey3)} />
{/* ... */}
</Foo>
)
}
}
Now I'm not creating a new function on every render but I'm calling a new function on every render.
Now I'm not sure which one to use. Is calling a function on every render a bad practice? Should I use a arrow function?
When using the curried function, you can use its closure on the current scope.
export default class MyComponent extends Component {
state = {
counter: 42
}
myFunc = dynamicKey => e => {
// closure on the specific this.state.counter value at time of render.
}
}
While returning a new function on every render, its closure is on the recent scope
export default class MyComponent extends Component {
state = {
counter: 42
}
myFunc = dynamicKey => {
// closure on this.state.counter value
}
}
Therefore, it depends on what is the use case.
Ask yourself if the function needs a specific value or the recent one.
Note: if on every render the functions re-declared, it becomes a "difference between function and curried one" question, and for React it doesn't matter as both functions bodies will be executed. So only by memoizing the function (don't call the function with it is called with the same parameters), you can get any noticeable differences.
You can cache your event handlers.
class SomeComponent extends React.Component {
// Each instance of SomeComponent has a cache of click handlers
// that are unique to it.
clickHandlers = {};
// Generate and/or return a click handler,
// given a unique identifier.
getClickHandler = (key) => {
// If no click handler exists for this unique identifier, create one.
if (!this.clickHandlers[key])){
this.clickHandlers[key] = () => alert(key);
}
return this.clickHandlers[key];
}
render() {
return (
<ul>
{this.props.list.map(listItem =>
<li key={listItem.text}>
<Button onClick={this.getClickHandler(listItem.text)} />
</li>
)}
</ul>
);
}
}
see the following article
If you use React hooks then:
const Button = props => {
const onClick = React.useMemo(() => {
alert(listItem.text)
}, [listItem.text]);
}
return <button onClick={onClick}>click</button>
}
If your function does not depend on your component (no this contexts), you can define it outside of the component. All instances of your component will use the same function reference, since the function is identical in all cases.
In contrast to the previous example, createAlertBox remains the same reference to the same location in memory during every render. Button therefore never has to re-render.
While Button is likely a small, quick-to-render component, you may see these inline definitions on large, complex, slow-to-render components, and it can really bog down your React application. It is good practice to simply never define these functions inside the render method.
If your function does depend on your component such that you cannot define it outside the component, you can pass a method of your component as the event handler:
In this case, each instance of SomeComponent has a different alert box. The click event listener for Button needs to be unique to SomeComponent. By passing the createAlertBox method, it does not matter if SomeComponent re-renders. It doesn’t even matter if the message prop changes! The address in memory of createAlertBox does not change, meaning Button does not have to re-render, and you save processing time and improve rendering speed of your application.
For dynamic functions
In this case, you have a variable number of buttons, making a variable number of event listeners, each with a unique function that you cannot possibly know what is when creating your SomeComponent. How can you possible solve this conundrum?
Enter memoization, or what may be easier to refer to as simply, caching. For each unique value, create and cache a function; for all future references to that unique value, return the previously cached function.
I am working on an existing React code that someone else wrote, and facing some performance issues in it. Consider the following code snippet:
//ComponentA.js
class ComponentA extends React.Component {
this.state = { someValue : 'dummy' }
// Other code
// We are using Babel, so class fields are OK
updateVal = e => this.setState({ someValue : e.target.value})
// fetchData makes an ajax call
fetchData = () => { fetch(this.state.someValue) }
render() {
return (
<ComponentB val={this.state.someValue}
updateVal={this.updateVal}
fetchData={this.fetchData}/>
)
}
//ComponentB.js
class ComponentB extends React.Component {
render() {
return (
// Other code
//Input is a component from a library
<Input onChange={(e) => { this.updateValue(e) } }
onBlur={this.props.fetchData} />
value={this.props.val}
)
}
}
Now the problem is that whenever user types in Input, the value gets printed after a few seconds. This is because ComponentA is actually a pretty big component (I know it's bad, but I don't want to refactor it now as it's huge and we don't have much time) and it gets re-rendered every time user types. To avoid this, I can make Input an uncontrolled component and update the ComponentA's someValue onBlur. Another way is to have an initialState in ComponentB which is equal to the val prop. And onChange, this.setState is called for ComponentB only. Then onBlur, I can update ComponentA's this.state.someValue.
However, in both these approaches, the principle of single source of truth of React is lost. So what will be the best solution in this case?
Here I would also like to ask, what's the harm in using Uncontrolled Component here?
A quickfix could be to debounce the onChange handler.
I usually use debounce from Lodash but you can use another one, or write your own version.
import { debounce } from 'lodash'
class ComponentB extends React.Component {
// componentA will rerender only every 300ms instead of every time user types
handleChange = debounce(e => this.props.updateVal(e), 300)
render() {
return <Input onChange={this.handleChange} value={this.props.val} />
}
}
The best solution would be to split/refactor componentA though.
The re-rendering is optimized (I don't think that the problem is the related to the component size).
I think that the issue is not caused by your structure (That is the one adviced by React itself) but from the Input component taken from the library.
You could also keep the state in the ComponentB and expose function to get/set values but then, as you said, you will lose the single source of trouth but you will avoid re-rendering every input change.
Hope this will help!
What is the point of this?
In the next example i found in book code we have a funtion in the component that changes component state createTimer()
createTimer = (timer) =>
{
const t = helpers.newTimer(timer);
this.setState({
timers: this.state.timers.concat(t),
});
client.createTimer(t);
};
It is wrapped:
handleCreateFormSubmit = (timer) => {
this.createTimer(timer); };
And passed down as property:
<ToggleableTimerForm
onFormSubmit={this.handleCreateFormSubmit}
/>
If you just do this:
<ToggleableTimerForm onFormSubmit={this.createTimer}/>
...and createTimer is a regular method of your class:
class YourComponent extends Component {
createTimer(timer) {
const t = helpers.newTimer(timer);
this.setState({
timers: this.state.timers.concat(t),
});
client.createTimer(t);
}
}
...then the issue would be that when the child component calls onFormSubmit, this wouldn't be set correctly.
But since you're setting a property of your instance and are using an arrow function:
class YourComponent extends Component {
createTimer = (timer) => {
const t = helpers.newTimer(timer);
this.setState({
timers: this.state.timers.concat(t),
});
client.createTimer(t);
};
}
...you don't have to worry about this being bound correctly, so you're right that you don't need the wrapper to fix that. Perhaps the wrapping function is there as a precautionary measure since the class method pattern is more common.
The only benefit you'd gain is if the child calls this.props.onFormSubmit with additional arguments that you want to ignore. If that's not the case, then you can leave out the wrapping function.
Generally you pass a function down that's bound to it's original component. This allows child components to alter the state of their parent. Imagine this Scenario :
I have a parent component with state property A. I have a function that takes an input and updates the state of the PARENT!!!!
I pass that as a prop to a child (maybe a form). When I submit the form, I call the function passed as a prop to update the PARENTS state with my form values.
A few things to keep in mind, lexical arrow functions LACK SCOPE, and if the function leverages the state of the component it must be bound to the component.
One problem I see in your code....
handleCreateFormSubmit requires a parameter. onFormSubmit will pas it one, but I don't think it'll be the one you're expecting. It'll pass the event. You can do something along these lines "
onFormSubmit={() => this.handleCreateFormSubmit(someValue)}
When I try to modify a base component's variable from a child component. I find that I can only do it by strictly doing the following:
1: Base component must have defined an event handler, strictly a variable onVariableChange event handler, and have it assigned to a local function
2: Base component must have custom attribute variable that will be linked with the above onVariableChange function
3: Child component can now call the this.props.onVariableChange() to make the appropriate modification (from child to base)
in Base declaration:
changeFn(){ //do Something }
Base's render:
return <div> <Child variable={this.stateSomeVar} onVariableChange={this.changeFn} />
in Child:
this.props.onVarChange();
Why is that? Why can't we just call the custom function from child to base directly without the use of custom property?
Am I incorrectly understanding the React's documentation?
in Base:
childFnAnsweredByBase(){
...
}
render(){
return <Child callFromChildFn={this.childFnAnsweredByBase} />
}
REF:
https://reactjs.org/docs/lifting-state-up.html
When I try to modify a base component's variable from a child
component. I find that I can only do it by strictly doing the
following:
I think you mean with variable, base or more accurate parent component's state.
1: Base component must have defined an event handler, strictly a
variable onVariableChange event handler, and have it assigned to a
local function
I don't know what do you mean by saying "strictly", but yes in order to do that parent should have a handler method. The name here is not important, just pass this properly to your child component.
2: Base component must have custom attribute variable that will be
linked with the above onVariableChange function
This variable or state property doesn't need to be linked anywhere and you don't have to pass this to your child component. If child component will use it yes you can pass, but in order to change this in the parent component, it is not needed to be passed to the child.
this.props.onVarChange();
Why is that? Why can't we just call the custom function from child to
base directly without the use of custom property?
If you mean saying by "property" the value itself, again, you don't need to pass it to the child. But, if you mean props, then you should use like that since this function is a part of the child's props.
Here is an example of how you do it without passing the "variable":
const Child = (props) => (
<div>
<input onChange={props.callFromChildFn} />
</div>
);
class App extends React.Component {
state = {
someVar: "initial value",
}
childFnAnsweredByBase = event =>
this.setState({ someVar: event.target.value })
render() {
return (
<div>
<Child callFromChildFn={this.childFnAnsweredByBase} />
<p>someVar is: {this.state.someVar}</p>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Am I incorrectly understanding the React's documentation?
Probably yes. Personally, I don't like that part of the documentation. They are trying to explain an edge case there. Two child components are syncing with some parent's state and show this value with an appropriate situation. Like, one of them shows this value as Fahrenheit and the other one shows it as Celcius. This is why they are passing the state variable (after some conversion) to these components.
In the example above we don't use this state variable in our child component, this is why we don't need it. Here is an example (just a simple, stupid example) showing that how can we use it and why the parent is passing it.
const Child = (props) => {
const { someNum, multiplyTheNumberBy, by } = props;
const handleMultiply = () => {
const newNum = someNum * by;
multiplyTheNumberBy( newNum );
}
return (
<div>
<button onClick={handleMultiply}>Multiply Number By {by}</button>
</div>
);
}
class App extends React.Component {
state = {
someNum: 1,
}
multiplyTheNumberBy = valueFromChild =>
this.setState({ someNum: valueFromChild })
render() {
return (
<div>
<Child
multiplyTheNumberBy={this.multiplyTheNumberBy}
someNum={this.state.someNum}
by={10}
/>
<Child
multiplyTheNumberBy={this.multiplyTheNumberBy}
someNum={this.state.someNum}
by={100}
/>
<p>someNum is: {this.state.someNum}</p>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Update after comments
Also, why do we have to assign const localFn = props.CallFromChildFn ?
why can't we just invoke this.props.CallFromChildFn directly? Or is it
supposed to be props.CallFromChildFn?
First things first. We use this in a class component, so for a functional component, it is not necessary. We can use our props as props.something instead of this.props.something.
Now, the second question is about applying the best practice for performance reasons. For a small app this may not be a problem but for larger apps which has multiple children, components may be problematic.
When defining your functions in a JSX prop, if you use an arrow function and invoke them immediately, or bind it to this there to use it properly, this function is recreated in every render. This is why use references to this functions instead of immediately invoke them somehow or use bind.
Examples.
Think about my first example.
<input onChange={props.callFromChildFn} />
Here, I used the reference of my function and it workes. Since I don't invoke any function here it is not recreated every time when my component renders. I would use it in this way:
<input onChange={e => props.callFromChildFn( e )} />
Here, we are using a callback for onChange as an arrow function. It takes an event and passes it to our callFromChildFn function. This works, too. But, since we used an arrow function here, this function is created in every render.
Let's see my second example.
const { someNum, multiplyTheNumberBy, by } = props;
const handleMultiply = () => {
const newNum = someNum * by;
multiplyTheNumberBy( newNum );
}
return (
<div>
<button onClick={handleMultiply}>Multiply Number By {by}</button>
</div>
Again, instead of using directly my function, I define a handler function here and use its reference. With this newly created function, I can do multiplication operation and use my multiplyTheNumber function from my props and pass it the calculated value. But again, I would use something like this:
const { someNum, multiplyTheNumberBy, by } = props;
return (
<div>
<button onClick={() => multiplyTheNumberBy(someNum * by)}>Multiply Number By {by}</button>
</div>
As you can see, without creating a new function we can use an onClick callback function and use our multiplyTheNumberBy from our props and do the multiplication directly there. But, this function also recreated in every render.
Yes, with the reference method we use a little more code and for small applications maybe this is not necessary. But, I like to use in this way.
I have a question regarding "one time actions" in react components. Imagine for example I want to scroll some element to certain position, or reset the internal react state.
So far I've been doing this by using a combination of a boolean flag (e.g. doAction: true) and an update action (e.g. setDoActionBackToFalse), but this seems too complex. Does anyone have any nice solution to this?
Note: The action can actually happen multiple times during the lifetime of the component but each time it has to be specifically triggered and happen only once (not keep happening on every rerender). E.g. scroll to every newly added item in scrollpane.
I created small fiddle to make the problem more obvious:
https://jsfiddle.net/martinkadlec/et74rkLk/1/
This uses the boolean flag approach.
It has been some time since I asked this question and since then I found that as long as the "one time action" doesn't actually rerender the component, but instead just modifies some browser state (e.g. focus, scroll position, etc.) people generally tend to solve this by having a class method and calling it from the parent component using refs.
To illustrate on the focus example:
class Input extends React.Component {
inputElRef = React.createRef();
focus = () => this.inputElRef.current.focus();
render() {
return (
<input ref={this.inputElRef} />
);
}
}
class Parent extends React.Component {
inputRef = React.createRef();
render() {
return (
<div>
<button onClick={() => this.inputRef.current.focus()}>Focus input</button>
<Input ref={this.inputRef} />
</div>
);
}
}
I think that you can use componentDidMount lifecycle hook. This hook is invoked only once immediately after a component is mounted and the DOM can be accessed in it.
You can also call your 'one time action' in component constructor but it's called before component is mounted and before initial render so you can't access DOM there.
So you can initialize component state in a constructor (according to React docs: constructor is the right place to initialize state) but you can't scroll some element to certain position in constructor because you can't access component DOM elements in it.
Summing up: state initialization should be done in constructor while 'one time actions' manipulating DOM should be done in componentDidMount.
Wrap your action handlers inside a higher order function which invokes them only once. Lodash has once. Ramda has it too.
Updates for your scrolling scenario.... Scrolling is a side effect which must be initiated by the DOM API. You can write an HOC which wraps any component inside it -
function OnFocusExtender(Wrapped){
return class ExtendedFocus{
focus = _.once(elem => elem && elem.focus && elem.focus());
render(){
return <Wrapped ref={this.focus} {...this.props} />;
}
}
}
Then you can use it in your code like -
render(){
let FocusedComponent = FocusExtender(YourComponent);
return <FocusedComponent a={"blah"} b={blah} />
}
Updated for a generic side-effects approach:
The HOC:
function WelcomingParty(...party)=>(Wrapped)=>{
return class ExtendWelcome{
// Every host in the welcoming party greets
// the guest :)
welcome = (ref) => party.forEach(host => host(ref));
render(){
return <Wrapped ref={this.welcome} {...this.props} />;
}
}
}
Usage:
let hostFn = (fn)=>(ref)=> ref && (typeof ref[fn] == "function") && ref[fn](),
hosts = ["focus", "scrollIntoView"].map(hostFn);
render(){
let Component = WelcomingParty(...hosts)(YourComponent);
return <Component a={"blah"} b={blah} />
}