Changing a Class String variable through another Class - Not working - javascript

Question: I want to press the "Manipulator" class button to change the ChangeValue's value variable to Manipulator's manipulatorName value, which isn't occurring. What have I done wrong?
I have a class (called ChangeValue) that initialises with an empty string name. I'm doing this since it will be displayed on the website as "Empty" for now. However, when I click a another class (called Manipulator) it should change ChangeValue, which isn't occurring. The ChangeValue's value is always set to "Empty", despite when I click the button.
My code:
export class ChangeValue extends React.Component {
constructor(props) {
super(props)
this.state = {
value: " "
};
}
render() {
var currentValue = null;
if (this.value == null) {
currentValue = "Empty"
} else {
currentValue = this.value
}
return (
currentValue
)
}
}
export class Manipulator extends React.Component {
constructor(props) {
super(props)
this.state = {
manipulatorName: "New Value"
};
this.handleClick = this.handleClick.bind(this);
}
render() {
return (
<button onClick = {this.handleClick}>
<ChangeValue value = {this.state.manipulatorName} />
</button>
)
}
}
I based the "ChangeValue value" line based off what I was reading from Stack and it may be there is an issue with Parent/Children, too?

There are a few things going on here as to why it's not working.
You can clean up your ChangeValue component, as it doesn't actually use any state. It only needs to use the value of the prop passed to it from the Manipulator, so therefore can be converted into a stateless 'dumb' function based component.
function ChangeValue(props) {
return props.value || "Empty"
}
Or if you still want it as a class so you can add some state in later...
export class ChangeValue extends React.Component {
render() {
return this.props.value || "Empty"
}
}
These will both have the same output. They will return the value of props.value if it is truthy, OR (||) return the word "Empty" if props.value is falsy.
The Manipulator class needs a little bit of work also. It currently sets up a handleClick method but doesn't define it. It should probably look something like this...
export class Manipulator extends React.Component {
constructor(props) {
super(props);
this.state = {
manipulatorName: undefined
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
manipulatorName: "New Value After Click"
});
}
render() {
return (
<button onClick={this.handleClick}>
<ChangeValue value={this.state.manipulatorName} />
</button>
);
}
}
This will initially render a button with the text "Empty" as the manipulatorName is *falsy*. Upon clicking the button, the button text should then change to say "New Value After Click".
I hope this helps, if it doesn't quite fit with what you're trying to achieve, please comment or update the question with some further details.
UPDATE
Here is a working example on CodeSandbox.

Related

How to update state of a parent while using its another state without causing infinite updates?

Let's have components A and B.
Component B only shows the parent's state in contentForB in a different format and doesn't manipulate with it. It takes A's state as a prop, applies a function transform(content) and shows it, so whenever A's contentForB changes, the new content get transformed and updated in B.
The problem comes when A wants to use B's transformed content and use it somewhere else. I tried to implemented in a standard way, using state-updating function and passed it from A to B like this:
class A extends React.Component {
constructor(props) {
super(props);
this.state = {
contentForB: "",
transformedContent: ""
};
this.updateTransformedContent = this.updateTransformedContent.bind(this);
}
updateTransformedContent(newContent) {
this.setState = { transformedContent: newContent };
}
render() {
return (
...
<B content={ this.state.contentForB }
updateTransformedContent={ this.updateTransformedContent } />
<ComponentUsingTransformedContent transformedContent = { this.state.transformedContent } />
);
}
}
class B extends React.Component {
constructor(props) {
super(props);
this.transform = this.transform.bind(this);
}
transform(content) {
let newContent = ...; ​
​this.props.updateTransformedContent(newContent);
​return newContent;
​}
​render() {
​<Something value={this.transform(this.props.content)} />
​}
}
However, when A's state changes, B gets reinitialized, it then changes A's state by calling the updateTransformedContent which again causes B to get reinitialized, thus causing an infinite loop even though the updateTransformedContent changes the state object which isn't directly passed into B.
Any ideas how to deal with such situation properly?
You can use componentDidUpdate lifecycle method.
[codesandbox.io]
https://codesandbox.io/s/cool-leaf-92nt8?file=/src/App.js:457-475
class TestB extends Component {
constructor(props) {
super(props);
this.state = {
transformed: ""
};
this.transform = this.transform.bind(this);
}
componentDidUpdate(prevProps) {
if (this.props.content !== prevProps.content) {
this.transform(this.props.content);
}
}
transform(content) {
const transformed = `<h4>${this.props.content}</h4>`;
this.setState({ transformed: transformed });
this.props.updateTransformedContent(transformed);
return transformed;
}
render() {
return (
<div>
<h4>B Componenet:</h4>
<p>{this.state.transformed}</p>
</div>
);
}
}

Propogating Events to Child Components in React

I want to send events down to my React child.
I feel like this is kind of an easy thing to do, so maybe i just have a mental block, and there is something obvious that is staring me in the face.
Anyway, I have a little Test app which illustrates the problem:
export class Test extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
let {buttonClicked, textFieldChanged} = this.state
return (
<div>
<button onClick={()=>this.handleClick()}>
Click
</button>
<input type={"text"} onChange={()=>this.handleTextChange()}/>
<Inner buttonClicked={buttonClicked} textFieldChanged={textFieldChanged}/>
</div>
);
}
handleClick(e) {
this.setState({ buttonClicked: true })
}
handleTextChange(e) {
this.setState({textFieldChanged:true})
}
}
class Inner extends React.Component {
render() {
let {buttonClicked, textFieldChanged} = this.props;
return (
<React.Fragment>
<div>Clicked : {buttonClicked ? "CLICKED!" : " "}</div>
<div>Text input : {textFieldChanged ? "TYPED!" : " "}</div>
</React.Fragment>
);
}
}
A button and a textfield live in the parent. Both these widgets can fire off events and change the child component.
This is simply achieved by passing a state value as a property down to the child. Very easy stuff.
However I would like an either/or situation. When I click the button this removes the text event, and vice versa. Ie. I do not want to see a situation like this :
Now there is a very obvious way to fix this by changing the state value to "false" of the other value.
handleClick(e) {
this.setState({ buttonClicked: true, textFieldChanged: false })
}
handleTextChange(e) {
this.setState({textFieldChanged:true, buttonClicked: false})
}
Is there any OTHER way of doing this?
The problem is that I have LOTS and LOTS of even handlers in my component and I don't want to negate the other state properties of the other values.
if i understood you correctly just one function will help - pass the attribute name into it
handleClick(propName) {
this.setState({
...this.state,
[propName]: !this.state[propName]
})
}
Create property lastEventType in parent component state , whenever you click or type - update it. And pass only this property to Inner component

changes in state array does not change children through props

Recently, I have been working with a dynamic control of input text boxes, each with their own id's. However, I noticed that if I stored an array of elements and each element was an object with different variables and such, any change in on of those variables in the object would not alert React to update the children components(which receive the state through props). I am trying to control the input according to the documentation, but am confused as to why changes in the parent's state of a normal non array and non object variable will be recognized by the children as a change in props and yet not for normal state arrays. Here is my parent code:
import React, {Component} from 'react';
import Music from './music'
import axios from 'axios';
import update from 'immutability-helper';
class Selection {
constructor(){
this.music = '';
this.beginning = '';
this.the_end = '';
}
setTitle=(title)=>{
this.music = title;
}
setStart=(start)=>{
this.beginning = start;
}
setEnd=(end)=>{
this.the_end = end;
}
}
class Practice extends React.Component{
constructor(props){
super(props);
this.state = {
selections: Array(0),
test: 0,
}
this.removeSong = this.removeSong.bind(this);
}
removeSong(index){
var newArray = this.state.selections.slice();
newArray.splice(index, 1);
this.setState({selections: newArray, test: this.state.test+=1});
}
addAnotherSong=()=>{
var newArray = this.state.selections.slice();
newArray.push(new Selection());
this.setState({ selections: newArray, test: this.state.test+=1});
}
render(){
return(
<div>
<button onClick={() => this.props.practice()}>Log Practice Session</button>
<h1>{this.props.time}</h1>
<form >
Description: <input type="form" placeholder="How did it go?" name="fname"/><br/>
</form>
<button onClick={()=>this.addAnotherSong()}>Add Another Piece</button>
<button onClick={()=>this.setState({test: this.state.test})}>Will it now Update?</button>
{
this.state.selections.map((child, index) => (
this.state.selections[index].music,
<Music key={index} number={index} subtract={this.removeSong}
Title={this.state.test} Start={this.state.selections[index].beginning}
End={this.state.selections[index].the_end} changeTitle={this.state.selections[index].setTitle}
changeStart={this.state.selections[index].setStart} changeEnd={this.state.selections[index].setEnd}
/>
))
}
</div>
);
}
}
export default Practice;
As you can see, I have an array in the state with objects constructed from the Selection class. Here are the children that won't update unless the change happens in the non array type "this.state.test" prop.
import React, {Component} from 'react';
import InputBox from './input';
class Music extends React.Component{
constructor(props){
super(props);
}
shouldComponentUpdate(newProps){
if(this.props !== newProps){
return true;
} else{
console.log("wassup");
return false;
}
}
render(){
return(
<div>{this.props.number}
<InputBox cValue={this.props.Title} identity={this.props.number} updateInput={this.props.changeTitle} />
<InputBox cValue={this.props.Start} identity={this.props.number} updateInput={this.props.changeStart} />
<InputBox cValue={this.props.End} identity={this.props.number} updateInput={this.props.changeEnd} />
<button onClick={()=> this.props.subtract(this.props.number)}>DELETE{this.props.number}</button>
{this.props.Title}
</div>
)
}
}
export default Music;
Lastly, here is the children of that child.
import React,{Component} from 'react';
class InputBox extends React.Component{
constructor(props){
super(props);
this.state = { value: '' }
this.handleChange = this.handleChange.bind(this);
}
handleChange(event){
this.setState({value: event.target.value});
this.props.updateInput(this.state.value, this.props.identity);
console.log("test" + this.props.cValue);
}
shouldComponentUpdate(newProps){
if(this.props !== newProps){
console.log("Updating Input Component");
return true;
} else {
console.log("yo");
return false;
}
}
render(){
return(
<input type="text"onChange={this.handleChange} value={this.props.cValue}/>
)
}
}
export default InputBox;
If this is a bad question, please let me know. But, any help is always appreciated. Thankyou
You're treating your state as a mutable object - states in React should never be mutated, and a state change should always be a brand new object.
Take, for example, we have just one Selection in state.selections:
[ { music: 'music', beginning: 'beginning', the_end: 'ending' } ]
If the Music component ends up calling setStart('newBeginning'), then we end up with this in our state:
[ { music: 'music', beginning: 'newBeginning', the_end: 'ending' } ]
This looks like everything went well, but React won't actually pick up this change by default because state.selections is still referring to the same array.
To fix it, you're going to have to figure out how to update your array reference while you modify individual items. You've already done this in removeSong and addAnotherSong by calling slice on your current state. Slice returns a new array, so no issues there.
I'd recommend having methods to modify individual Selections in Practice, as opposed to each Selection modifying itself.
Something like this could work:
class Practice extends React.Component{
updateSelectionBeginning(index, newBeginning){
var newArray = this.state.selections.slice();
newArray[index].beginning = newBeginning;
this.setState({selections: newArray});
}
}
We'd then pass it down to our children via props, and they'd call updateSelectionBeginning(FOO, BAR). That lets us update the state, while also keeping it immutable.

How to update the value a component's state variable in another component in React.js?

I want to update the value of 'change_color' in the second class and automatically render it in the first class when the value gets changed.
Assume, 'Second' component as the child of the 'First' component.
Solved it. Code is edited and it is the answer.
class First extends Component {
constructor() {
super();
this.state = {
change_color: false
}
this.handleChange = this.handleChange.bind(this);
}
handleChange() {
this.setState({
change_color: true
})
}
render() {
console.log(this.state.change_color);
return(<div><Second colorChange={this.handleChange} /></div>)
}
}
class Second extends Component {
constructor() {
super();
}
render() {
return(<div><button onClick={this.props.colorChange} /></div>)
}
}
Maybe you can try this, just make a container component, and set the value you want to change into a state of the container component, add a method to change the state value, then, you can use "this.props.handleColorChange" to call the method of the parent component in children components.
class ParentComponent extends Component {
constructor() {
super();
this.state = {
change_color: false
}
}
handleColorChange= () => {
const {change_color} = this.state;
this.setState = {
change_color: !change_color
}
}
render() {
const {change_color} = this.state,
{handleColorChange} = this;
return (
<div>
<ChildComponent1
color={change_color}
handleColorChange={handleColorChange}
/>
<ChildComponent2
color={change_color}
handleColorChange={handleColorChange}
/>
</div>
);
}
}
class ChildComponent1 extends Component {
constructor() {
super();
}
render() {
const {color} = this.props;
return(
<span>now, the color is {color}</span>
)
}
}
class ChildComponent2 extends Component {
constructor() {
super();
}
const {handleColorChange} = this.props;
return(
<button onClick={handleColorChange}>click to change color</button>
)
}
What you need to do is lifting up the state. Create a new component that has a state with the colour and the change colour function. Then pass to first and second componentes the corresponding properties as props and inside of them call the function to change the colour. Does it makes sense?

React doesn't focus conditionally rendered input/textarea

I'm trying to focus() a conditionally rendered textarea in React. The code below is almost exactly identical to the example in the React docs or to this similar question.
The code below immediately shows and focuses the textarea. If the three commented lines are uncommented, the textarea gets shown after the condition prop is set to true (its value depends on the state of the parent and is initially false), but the element doesn't get focused anymore.
If the condition is initially true, the input element gets focused as expected when the component renders for the first time. The problem occurs when the condition is changed from false to true.
import React, { Component } from 'react'
class TestClass extends Component {
constructor(props) {
super(props)
this.focus = this.focus.bind(this)
}
componentDidMount() {
// this.props.condition &&
this.focus()
}
componentDidUpdate() {
// this.props.condition &&
this.focus()
}
focus() {
console.log(`this.textInput: ${this.textInput}`)
this.textInput.focus()
}
render() {
return (
<div>
{
// this.props.condition &&
<textarea
ref={(input) => {this.textInput = input}}
defaultValue="Thanks in advance for your invaluable advice">
{console.log('textarea rendered')}
</textarea>
}
</div>
)
}
}
The console output
textarea rendered
this.textInput: [object HTMLTextAreaElement]
rules out that the element is unavailable at the time focus() gets executed.
Furthermore:
Setting autoFocus attribute doesn't seem to work, in contrast to this question
Same problem for both <input /> and <textarea />
Edit: in response to the question below, the parent component looks as follows.
class ParentComponent extends Component {
constructor(props) {
super(props)
this.state = {
condition: false
}
this.toggleCondition = this.toggleCondition.bind(this)
}
toggleCondition() {
this.setState(prevState => ({
condition: !prevState.condition
}))
}
render() {
return (
<div>
<TestClass condition={this.state.condition} />
<button onMouseDown={this.toggleCondition} />
</div>
)
}
}
import React, { Component } from 'react';
class TestClass extends Component {
constructor(props) {
super(props);
this.focus = this.focus.bind(this);
}
componentDidMount() {
this.focus();
}
componentWillReceiveProps(nextProps) {
if (nextProps.condition !== this.props.condition) {
this.focus();
}
}
focus() {
console.log(`this.textInput: ${this.textInput}`);
if (this.props.condition === true) {
this.textInput.focus();
}
}
render() {
return (
<div>
{
this.props.condition &&
<textarea
ref={(input) => { this.textInput = input; }}
defaultValue="Thanks in advance for your invaluable advice"
>
{console.log('textarea rendered')}
</textarea>
}
</div>
);
}
}
export default TestClass;
Turns out the problem was really really stupid. The way I implemented the toggle button (which I simplified to <button onClick={this.toggleCondition} /> in the original question "for clarity"), was with a custom component which takes the onClick prop and attaches its value to the onMouseDown attribute of a hyperlink.
Because the hyperlink gets focused after its onMouseDown action, the focus was immediately taken away from the textarea.
I've edited the question to reflect my usage of onMouseDown.

Categories