Proper usage of the extendability of "this" in React component - javascript

We could briefly describe that this.props is data flow from parent component and this.state is for keeping the current state of the component, and the mechanism we massively depend when we develop in React is re-rendering after setState().
If my understanding of the usage of these two are not wrong,
except holding function object, is it proper to utilize the extendability of this to hold some values considered as global variables?
For example, if I want to make 'swipe' manner available on my component, I may could do something like:
class Slider extends React.Component {
constructor(props) {
super(props);
this.state = {
movement: 0,
touchStartX: 0,
prevTouchX: 0,
beingTouched: false
};
this.handleTouchStart = this.handleTouchStart.bind(this);
this.handleTouchMove = this.handleTouchMove.bind(this);
this.handleTouchEnd = this.handleTouchEnd.bind(this);
}
handleTouchStart(e) {
this.setState({
touchStartX: e.targetTouches[0].clientX,
beingTouched: true
});
}
handleTouchMove(e) {
if (this.state.beingTouched) {
let deltaX = e.targetTouches[0].clientX - this.state.touchStartX;
this.setState({
movement: deltaX,
prevTouchX: e.targetTouches[0].clientX
});
}
}
handleTouchEnd(e) {
// handle the sliding and set state touchStartX and beingTouched to 0 and false.
}
render() {
return (<div className = 'sliderBox'
onTouchStart = {e => this.handleTouchStart(e)}
onTouchMove = {e => this.handleTouchMove(e)}
onTouchEnd = {e => this.handleTouchEnd(e)}></div>);
}
}
export default Slider;
This is a part of my built application, it just works well. But I still wonder if it's a good way to use state property.
Or it's just OK to do something like:
class Slider extends React.Component {
constructor(props) {
super(props);
this.movement = 0;
this.touchStartX = 0;
this.prevTouchX = 0;
this.beingTouched = false;
this.handleTouchStart = this.handleTouchStart.bind(this);
this.handleTouchMove = this.handleTouchMove.bind(this);
this.handleTouchEnd = this.handleTouchEnd.bind(this);
}
handleTouchStart(e) {
this.touchStartX = e.targetTouches[0].clientX;
this.beingTouched = true;
}
handleTouchMove(e) {
if (this.beingTouched) {
let deltaX = e.targetTouches[0].clientX - this.state.touchStartX;
this.movement = deltaX;
this.prevTouchX = e.targetTouches[0].clientX;
}
}
handleTouchEnd(e) {
// handle the sliding and set state touchStartX and beingTouched to 0 and false.
}
render() {
return (<div className = 'sliderBox'
onTouchStart = {e => this.handleTouchStart(e)}
onTouchMove = {e => this.handleTouchMove(e)}
onTouchEnd = {e => this.handleTouchEnd(e)}></div>);
}
}
export default Slider;
But it seems that the utilization of the extendability of this above is rarely seen?
Sorry if my question is meaningless, I just wonder if is there any spirit or principle to utilize the extendability of this? Props and cons?

Yes, you can attach variables directly to the component's this. It's proper in your use case.
In React's Documentation itself, in the state and lifecycle section, it gives an example of storing a timer id directly in this:
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
Note how we save the timer ID right on this.
While this.props is set up by React itself and this.state has a special meaning, you are free to add additional fields to the class manually if you need to store something that is not used for the visual output.
If you don’t use something in render(), it shouldn’t be in the state.
We will tear down the timer in the componentWillUnmount() lifecycle hook:
componentWillUnmount() {
clearInterval(this.timerID);
}
And as of Gleb Kost's answer, I agree that it's a normal practice.
You've nailed it. If it doesn't make sense to be in props, neither in state, feel free to attach it directly to this, if it also makes sense.

It seems that extendability of this in the react component is a normal practice, I've seen it being used in almost every more or less complex React project I have worked on.
As for deciding where to put the data in the state or on the component itself, I usually ask myself a question: does the component need to react to the change of those properties? If yes, they are going in the state, if not - on the component.
In your case, since you are using those properties only in event handlers and don't really need the component to re-render every time they change, I'd say optimal would be to utilise this, as you do.
On the other hand, if you want to use beingTouched property in the render method, for example, to change the background color of the component when it's touched than you need to put it in the state, otherwise the component would not react as expected, because it will be unaware that the property has changed.

Related

React: Tell child component to "reinitialize," even when the passed props are the same

I have a MyComponent that renders a Timer component. My current setup is like this:
MyComponent.render:
render () {
return <Timer time={this.state.time} lag={this.lag || 0} />
}
Timer:
class Timer extends Component {
constructor(props) {
super(props);
this.state = {
time: this.props.time,
};
}
startTimer = (duration) => {
if (duration > 0){
this.on = true;
let timer = duration * 1000 + this.props.lag;
var s = setInterval(() => {
this.setState({time: Math.round(timer/1000)});
timer = timer - 500;
if (timer <= 0) {
this.on = false;
clearInterval(s);
}
}, 500);
}
}
componentDidMount = () => {
this.startTimer(this.props.time);
}
render() {
return (
<div className="Timer-container">
<div className="Timer-value">{this.state.time}</div>
</div>
);
}
}
As you can see, when the Timer is initialized, it immediately starts counting down. On subsequent renders of MyComponent, I want to restart the Timer, even if the time prop doesn't change. In other words, I want it to "reinitialize" on every render. How do I achieve this?
First of all, to reset the counter, you need to store something in the state,
either the interval (so you can clear it)
or the current time (so you can set it to the initial value).
As you want to do something if the parent re-rendered (but the props didn't change), basically what you need to check is why your component updated. An answer to that would be "Trace why a React component is re-rendering"
A quick way for your example would be to check if the state has changed (not recommended):
componentDidUpdate(prevProps, prevState, snapshot) {
if( prevState === this.state ){
clearInterval( this.state.interval );
this.startTimer( this.props.time );
}
}
Another quick solution would be (if it is an option for you) to pass a shouldRerender property to the component, and then check for this property inside the component:
// -- inside MyComponent
render () {
return <Timer
time={ this.state.time }
lag={ this.lag || 0 }
shouldRerender={ {/* just an empty object */} } />;
}
// -- inside Timer
componentDidUpdate(prevProps, prevState, snapshot) {
if( prevProps.shouldRerender !== this.props.shouldRerender ){
clearInterval( this.state.interval );
this.startTimer( this.props.time );
}
}
That looks a bit "dirty" to me. A cleaner way would be to pass some state to shouldRerender, which changes on every update (e.g. just an increasing number).
However, I think the approach to check if parent rendered is not the React way. I, personally, do consider if a component renders or not an implementation detail (I don't know if that's correct to say), that is, I don't care when React decides to render, I only care for props and state (basically).
I would recommend to think about what actually is "cause and effect", what is the reason why you want to reset the timer. Probably the re-render of the parent is only the effect of some other cause, which you might be able to use for your time reset, too.
Here some different concepts that might be useful for use cases I can imagine:
not use one Time instance, but destroy and create inside parent when needed, maybe also using a key prop.
use a HOC (like withTimer) or custom hook (like useTimer), injecting a reset() function (plus create a separate TimerView component)
keep the time state in MyComponent, passing time and onChange down to the Timer component (<Timer time={ this.state.time } onChange={ time => { this.setState({ time: time }); } } />), then both MyComponent and Timer can set / reset the time.

How does React.useState triggers re-render?

import { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
In the above example whenever setCount(count + 1) is invoked a re-render happens. I am curious to learn the flow.
I tried looking into the source code. I could not find any reference of useState or other hooks at github.com/facebook/react.
I installed react#next via npm i react#next and found the following at node_modules/react/cjs/react.development.js
function useState(initialState) {
var dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
On tracing back for dispatcher.useState(), I could only find the following ...
function resolveDispatcher() {
var dispatcher = ReactCurrentOwner.currentDispatcher;
!(dispatcher !== null) ? invariant(false, 'Hooks can only be called inside the body of a function component.') : void 0;
return dispatcher;
}
var ReactCurrentOwner = {
/**
* #internal
* #type {ReactComponent}
*/
current: null,
currentDispatcher: null
};
I wonder where can I find dispatcher.useState() implementation and learn how it triggers re-render when setState setCount is invoked.
Any pointer would be helpful.
Thanks!
The key in understanding this is the following paragraph from the Hooks FAQ
How does React associate Hook calls with components?
React keeps track of the currently rendering component. Thanks to the Rules of Hooks, we know that Hooks are only called from React components (or custom Hooks — which are also only called from React components).
There is an internal list of “memory cells” associated with each component. They’re just JavaScript objects where we can put some data. When you call a Hook like useState(), it reads the current cell (or initializes it during the first render), and then moves the pointer to the next one. This is how multiple useState() calls each get independent local state.
(This also explains the Rules of Hooks. Hooks need to be called unconditionally in the same order, otherwise the association of memory cell and hook is messed up.)
Let's walk through your counter example, and see what happens. For simplicity I will refer to the compiled development React source code and React DOM source code, both version 16.13.1.
The example starts when the component mounts and useState() (defined on line 1581) is called for the first time.
function useState(initialState) {
var dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
As you have noticed, this calls resolveDispatcher() (defined on line 1546). The dispatcher refers internally to the component that's currently being rendered. Within a component you can (if you dare to get fired), have a look at the dispatcher, e.g. via
console.log(React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher.current)
If you apply this in case of the counter example, you will notice that the dispatcher.useState() refers to the react-dom code. When the component is first mounted, useState refers to the one defined on line 15986 which calls mountState(). Upon re-rendering, the dispatcher has changed and the function useState() on line 16077 is triggered, which calls updateState(). Both methods, mountState() on line 15352 and updateState() on line 15371, return the count, setCount pair.
Tracing ReactCurrentDispatcher gets quite messy. However, the fact of its existence is already enough to understand how the re-rendering happens. The magic happens behind the scene. As the FAQ states, React keeps track of the currently rendered component. This means, useState() knows which component it is attached to, how to find the state information and how to trigger the re-rendering.
setState is a method on the Component/PureComponent class, so it will do whatever is implemented in the Component class (including calling the render method).
setState offloads the state update to enqueueSetState so the fact that it's bound to this is really only a consequence of using classes and extending from Component. Once, you realize that the state update isn't actually being handled by the component itself and the this is just a convenient way to access the state update functionality, then useState not being explicitly bound to your component makes much more sense.
I also tried to understand the logic behind useState in a very simplified and basic manner, if we just look into its basic functionalities, excluding optimizations and async behavior, then we found that it is basically doing 4 things in common,
maintaining of State, primary work to do
re-rendering of the component through which it get called so that caller component can get the latest value for state
as it caused the re-rendering of the caller component it means it must maintain the instance or context of that component too, which also allows us to use useState for multiple component at once.
as we are free to use as many useState as we want inside our component that means it must maintain some identity for each useState inside the same component.
keeping these things in mind I come up with the below snippet
const Demo = (function React() {
let workInProgress = false;
let context = null;
const internalRendering = (callingContext) => {
context = callingContext;
context();
};
const intialRender = (component) => {
context = component;
workInProgress = true;
context.state = [];
context.TotalcallerId = -1; // to store the count of total number of useState within a component
context.count = -1; // counter to keep track of useStates within component
internalRendering(context);
workInProgress = false;
context.TotalcallerId = context.count;
context = null;
};
const useState = (initState) => {
if (!context) throw new Error("Can only be called inside function");
// resetting the count so that it can maintain the order of useState being called
context.count =
context.count === context.TotalcallerId ? -1 : context.count;
let callId = ++context.count;
// will only initialize the value of setState on initial render
const setState =
!workInProgress ||
(() => {
const instanceCallerId = callId;
const memoizedContext = context;
return (updatedState) => {
memoizedContext.state[instanceCallerId].value = updatedState;
internalRendering(memoizedContext);
};
})();
context.state[callId] = context.state[callId] || {
value: initState,
setValue: setState,
};
return [context.state[callId].value, context.state[callId].setValue];
};
return { useState, intialRender };
})();
const { useState, intialRender } = Demo;
const Component = () => {
const [count, setCount] = useState(1);
const [greeting, setGreeting] = useState("hello");
const changeCount = () => setCount(100);
const changeGreeting = () => setGreeting("hi");
setTimeout(() => {
changeCount();
changeGreeting();
}, 5000);
return console.log(`count ${count} name ${greeting}`);
};
const anotherComponent = () => {
const [count, setCount] = useState(50);
const [value, setValue] = useState("World");
const changeCount = () => setCount(500);
const changeValue = () => setValue("React");
setTimeout(() => {
changeCount();
changeValue();
}, 10000);
return console.log(`count ${count} name ${value}`);
};
intialRender(Component);
intialRender(anotherComponent);
here useState and initialRender are taken from Demo. intialRender is use to call the components initially, it will initialize the context first and then on that context set the state as an empty array (there are multiple useState on each component so we need array to maintain it) and also we need counter to make count for each useState, and TotalCounter to store total number of useState being called for each component.
FunctionComponent is different. In the past, they are pure, simple. But now they have their own state.
It's easy to forget that react use createElement wrap all the JSX node, also includes FunctionComponent.
function FunctionComponent(){
return <div>123</div>;
}
const a=<FunctionComponent/>
//after babel transform
function FunctionComponent() {
return React.createElement("div", null, "123");
}
var a = React.createElement(FunctionComponent, null);
The FunctionComponent was passed to react. When setState is called, it's easy to re-render;

Whats the right way to manipulate a model instance in React?

So I have a React component that accepts an instance of a function constructor (a Car).
The component's job is to display information about the Car and manipulate it based on the Car's public interface (methods and properties).
In the example below, a child component should add an accident on button click.
Question: What is the right way for the child to manipulate properties of the Car instance? The root parent's state stores reference to the instance of the Car, and the children are able to manipulate the Car's properties (like .accidents), but see the various onChange examples for why I'm struggling to find the right React way to do this.
I'd like to avoid a heavy handed solution like Flux to store this state.
Any suggestions would be appreciated!
function Car(name, color) {
this.name = name;
this.color = color;
this.accidents = [];
}
const myCar = new Car('Ferrari', 'Red');
myCar.accidents.push('accident #1');
class Accident extends React.Component {
handleButton1 = () => {
const newAccident = 'accident type1 # ' + Math.floor(Math.random()*100);
this.props.onChange1(newAccident);
}
handleButton2 = () => {
const newAccident = 'accident type2 # ' + Math.floor(Math.random()*100);
this.props.onChange2(newAccident);
}
handleButton3 = () => {
const newAccident = 'accident type3 # ' + Math.floor(Math.random()*100);
this.props.accidents.push(newAccident);
this.props.onChange3();
}
handleButton4 = () => {
const newAccident = 'accident type4 # ' + Math.floor(Math.random()*100);
this.props.accidents.push(newAccident);
// This circumvents React's state management, so the parent doesnt
// rerender when its state changes.
}
render() {
return (
<div>
<button onClick={this.handleButton1}>
Add accident (onChange1)
</button>
<button onClick={this.handleButton2}>
Add accident (onChange2)
</button>
<button onClick={this.handleButton3}>
Add accident (onChange3)
</button>
<button onClick={this.handleButton4}>
Add accident (option 4)
</button>
<ul>
{this.props.accidents.map((a, i) => <li key={i}>{a}</li>)}
</ul>
</div>
)
}
}
class DisplayCard extends React.Component {
state = {
editingCar: this.props.car
}
// Push the new accident into state and set it with the same reference.
onChange1 = (newAccident) => {
this.state.editingCar.accidents.push(newAccident);
// Is this semantically different than calling this.forceUpdate?
this.setState({
editingCar: this.state.editingCar,
});
}
// Clone the existing state we want to update and explicitly set that new state
onChange2 = (newAccident) => {
const newAccidentList = _.cloneDeep(this.state.editingCar.accidents);
newAccidentList.push(newAccident);
// Setting our new accident list like this converts editingCar to a POJO
// editingCar.name is lost because a deep merge does not happen.
this.setState({
editingCar: {
accidents: newAccidentList
},
});
}
// Just force update - this.state.editingCar was manipulated by <Accident />.
onChange3 = () => {
this.forceUpdate();
}
render() {
return (
<div>
<div>Car Name: {this.state.editingCar.name}</div>
<Accident
accidents={this.state.editingCar.accidents}
onChange1={this.onChange1}
onChange2={this.onChange2}
onChange3={this.onChange3}
/>
</div>
);
}
}
ReactDOM.render(
<DisplayCard car={ myCar } />,
document.getElementById('container')
);
Also on JSFiddle if you want to play around: https://jsfiddle.net/jamis0n003/fbkn5xdy/4/
EDIT: The React JS docs suggest integrating with "other libraries", such as Backbone models, using forceUpdate:
https://reactjs.org/docs/integrating-with-other-libraries.html#using-backbone-models-in-react-components
When state is stored in a parent component and a child component wants to manipulate that state, the parent should pass a callback function to the child's props. Then the child calls the callback to notify the parent to modify its own state. The child should never modify props since the change can have unintended consequences due to the way objects are referenced in JavaScript.
If you want to get really fancy, you can use Redux which stores "global" state in the top-most parent component. All child components issue (or dispatch) actions which notify the top-level parent to update its state which is then passed down again to all children components through their props.
What is the right way for the child to manipulate properties of the Car instance?
In general, rely on setState() to update state, which will reliably redraw the view, or if you mutate the data use forceRedraw() to ensure the view is redrawn with the latest data -- but using setState() is much preferred. In either case a child must notify a parent of a change using a callback like you have, but instead of having the child Accident actually change the data, make it a "dumb" component which notifies the parent of an intended change and the parent actually makes the change.
I'd like to avoid a heavy handed solution like Flux to store this state.
You may want to look into MobX, which is popular alternative to Flux/Redux that is a bit easier to get into because it allows you to mutate objects very much in the way you are already doing.

Create function that can pass a parameter without making a new component

My question is to do with the issue React has for binding functions in the render function.
The following is not good practice:
render() {
<div onClick={this.callFunction.bind(this)}/>
}
as each re render would add a new function to the page, eventually causing the browser to run out of memory.
The solution is to do this:
constructor() {
this.callFunction = this.callFunction.bind(this);
}
render() {
<div onClick={this.callFunction}/>
}
The problem with this is when I want to pass a value into the function.
I know I can make the div a child component, and pass the parameter in through the callBack, but this does not seem sensible if the div is only being used once in the whole application. I accept I could make this work, but this is not the scope of this question.
I also know that this solution:
render() {
<div onClick={() => this.callFunction.call(this, param)}/>
}
Is no better, as it is still creating a new function.
So my question is, how can I create a function that I can pass a parameter into without making a new component, and without binding a new funciton on each render?
You can't avoid creating a second component as you need to pass a function reference as an event handler, this will be executed by the browser when the event triggers.
So the problem is not the binding but the fact that you need to pass a reference, and references can't receive parameters.
EDIT
By the way, if you don't like the syntax and noise of binding or anonymous arrow functions you can use currying.
I posted an example in a different question if you find it interesting. this won't solve the problem though, it's just another approach to pass a new reference (which i find it to be the most terse)
You can change the declaration of callFunction to be an arrow function, which implictly binds the scope, like so:
callFunction = () => {
console.log('hi');
};
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
Then your original render function would work as expected!
Use Side effect
Side effect is something that a function use that comes from outside but not as argument. Now this mechanism is majorly used in Redux/Flux where the entire state is stored in a Store and every component fetches their state from it.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
handlerProps: {
onClick: { count: 0},
onChange: { count: 0}
}
}
}
onClickHandler = () => {
const state = this.state.handlerProps.onClick;
console.log('onClick', state.count);
}
onChangeHandler = (value) => {
const state = this.state.handlerProps.onChange;
console.log('onClick', state.count);
this.setState({value: value})
}
buttonClick = () => {
const random = Math.ceil(Math.random()* 10) % 2;
const handler = ['onClick', 'onChange'][random];
const state = this.state.handlerProps;
state[handler].count++;
console.log('Changing for event: ', handler);
this.setState({handlerProps: state});
}
render () {
return (
<div>
<input onClick={this.onClickHandler} onChange={this.onChangeHandler} />
<button onClick={ this.buttonClick }>Update Props</button>
</div>
)
}
}
ReactDOM.render(<MyComponent/>, document.querySelector('.content'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>
<div class='content' />
The only way I know of is to create a new React Component which takes the value and the event handler as props.
This way, the handler as a function remains static, and since the value is passed down separately (in its own prop) you don't have any functions being re-instanciated. Because you don't bind anything nor create a new function each time.
Here's an example:
We have two buttons. The first one prints the current state variable value and the other increments it by one.
Normally, if we had done this with onClick={() => this.print(this.state.value)} we would get a new instance of this function, each time the MyApp component would re-render. In this case, it would re-render each time we increment the value with the setState() inside this.increment.
However, in this example, no new instance of this.print happens because we are only passing its reference to the button. In other words, no fat arrow and no binding.
In the <Button /> component, we have a <button> to which event handler we pass a reference to a function - just like we did in <MyApp />. However, here we know exactly what to pass to the function. As such, we have myHandler trigger this.props.handler(this.props.value).
class MyApp extends React.Component {
constructor() {
super();
this.state = {
value: 0
};
}
print = (value) => {
console.log(value);
}
increment = () => {
// This will trigger a re-render, but none of the functions will be reinstanciated!
this.setState((prevState) => ({value: prevState.value + 1}));
}
render() {
// Note that both handlers below are passed just the references to functions. No "bind" and no fat arrow.
return(
<div>
<Button handler={this.print} value={this.state.value}>Print</Button>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
class Button extends React.Component {
// Clicking the button will trigger this function, which in turn triggers the function with the known argument - both of which were passed down via props.
myHandler = () => this.props.handler(this.props.value);
render() {
// Note again that the handler below is just given the reference to a function. Again, not "bind" nor fat arrow.
return(
<button onClick={this.myHandler}>{this.props.children}</button>
);
}
}
ReactDOM.render(<MyApp />, document.getElementById("app"));
<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="app"></div>
Though quite tedious, it is an effective solution. That being said, even if you do create a new function each time you render, the performance implications are minimal. From the official docs:
The problem with this syntax is that a different callback is created each time the LoggingButton renders. In most cases, this is fine.

Using setInterval in React delays component render

I have a parent component, <App>:
constructor() {
super();
this.state = {
transporterPos: 0
}
this.tick = this.tick.bind(this);
}
componentDidMount() {
this.timerId = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.timerId);
}
tick() {
let transporterPos = this.state.transporterPos;
transporterPos++;
if (transporterPos > 7) {
transporterPos = 0;
}
this.setState({ transporterPos: transporterPos });
}
render() {
return (
<div>
<Staves transporterPos={this.state.transporterPos}/>
</div>
);
}
The <Staves> component contains several <Stave> components, each of which contains several <Note> components. Each <Note> component is injected with a className conditional on its active property being true:
<div className="noteContainer" onClick={this.handleClick}>
<div className={"note" + (this.props.active ? ' active' : '')}></div>
</div>
handleClick() is a method that toggles a <Note>'s active property. I'm not including all the code here to make this more readable. The problem is that when clicking on a <Note>, although its active property changes immediately, the styling given by the conditional className of 'active' is not visible until the component is re-rendered at the next "tick" of the setInterval method. In other words, rendering only seems to happen once every 1000ms. I would like it to happen immediately. Am I using setInterval wrong?
Edit:
In response to comments, here is the handleClick method (in <Note>):
handleClick() {
this.props.toggleActive(this.props.pos);
}
This calls toggleActive in <Stave>:
toggleActive(pos) {
this.props.notes[pos].active = !this.props.notes[pos].active;
}
props.notes here is part of <App>'s state, which is passed down to <Stave> (and which I didn't include in this question for the sake of brevity).
toggleActive(pos) {
this.props.notes[pos].active = !this.props.notes[pos].active;
}
The reason a re-render isn't being triggered is because this.props is mutated directly instead of with setState. Move toggleActive further up to where you can use setState.
If necessary you can pass the function as a prop to the child component and call it via this.props.toggleActive()
Besides not triggering a re-render, another reason this.props should never be mutated directly is because your changes will get overwritten whenever the parent changes state and passes props to its children.

Categories

Resources