Pass function from parent class to function in child class in React - reactjs

I am building my first React app, it is a Quiz app. The state of the user's score is incremented by functions in the Parent class. I am having trouble accessing these increment functions from the child class.
I would like to use the increment and decrement functions in the Parent class to update the state of score.
My simplified code is below:
Parent
class App extends React.Component {
constructor(props){
super(props);
this.state = {
score: 0
};
}
increment = () => {
console.log('+')
this.setState({
score: this.state.score + 1
});
};
decrement = () => {
console.log('-')
this.setState({
score: this.state.score - 1
});
};
render() {
const choice1 = this.state.choice1;
const choice2 = this.state.choice2;
const score = this.state.score;
const correct_answer = this.state.correct_answer;
return (
<div>
<Mybutton
text={choice1}
score ={score}
correct_answer={correct_answer}
decrement = {this.decrement}
increment = {this.increment}
/>
</div>
<div>
<Mybutton
text={choice2}
score ={score}
correct_answer={correct_answer}
decrement = {this.decrement}
increment = {this.increment}
/>
</div>
);
}
}
Child
class Mybutton extends React.Component {
render() {
return (
<button onClick={ () => {this.handleClick()}}> {this.props.text} </button>
);
}
handleClick(){
if (this.props.text === this.props.correct_answer){
this.props.increment
} else {
this.props.decrement
};
}

Call the fuction with prentices this.props.increment()

Related

Call child component function from parent

How do I call a child component function from the parent component? I've tried using refs but I can't get it to work. I get errors like, Cannot read property 'handleFilterByClass' of undefined.
Path: Parent Component
export default class StudentPage extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
newStudentUserCreated() {
console.log('newStudentUserCreated1');
this.refs.studentTable.handleTableUpdate();
}
render() {
return (
<div>
<StudentTable
studentUserProfiles={this.props.studentUserProfiles}
ref={this.studentTable}
/>
</div>
);
}
}
Path: StudentTable
export default class StudentTable extends React.Component {
constructor(props) {
super(props);
this.state = {
studentUserProfiles: props.studentUserProfiles,
};
this.handleTableUpdate = this.handleTableUpdate.bind(this);
}
handleTableUpdate = () => (event) => {
// Do stuff
}
render() {
return (
<div>
// stuff
</div>
);
}
}
UPDATE
Path StudentContainer
export default StudentContainer = withTracker(() => {
const addStudentContainerHandle = Meteor.subscribe('companyAdmin.addStudentContainer.userProfiles');
const loadingaddStudentContainerHandle = !addStudentContainerHandle.ready();
const studentUserProfiles = UserProfiles.find({ student: { $exists: true } }, { sort: { lastName: 1, firstName: 1 } }).fetch();
const studentUserProfilesExist = !loadingaddStudentContainerHandle && !!studentUserProfiles;
return {
studentUserProfiles: studentUserProfilesExist ? studentUserProfiles : [],
};
})(StudentPage);
My design here is: component (Child 1) creates a new studentProfile. Parent component is notified ... which then tells component (Child 2) to run a function to update the state of the table data.
I'm paraphrasing the OP's comment here but it seems the basic idea is for a child component to update a sibling child.
One solution is to use refs.
In this solution we have the Parent pass a function to ChildOne via props. When ChildOne calls this function the Parent then via a ref calls ChildTwo's updateTable function.
Docs: https://reactjs.org/docs/refs-and-the-dom.html
Demo (open console to view result): https://codesandbox.io/s/9102103xjo
class Parent extends React.Component {
constructor(props) {
super(props);
this.childTwo = React.createRef();
}
newUserCreated = () => {
this.childTwo.current.updateTable();
};
render() {
return (
<div className="App">
<ChildOne newUserCreated={this.newUserCreated} />
<ChildTwo ref={this.childTwo} />
</div>
);
}
}
class ChildOne extends React.Component {
handleSubmit = () => {
this.props.newUserCreated();
};
render() {
return <button onClick={this.handleSubmit}>Submit</button>;
}
}
class ChildTwo extends React.Component {
updateTable() {
console.log("Update Table");
}
render() {
return <div />;
}
}

ReactJS. Infinity loop

I'am getting props from child in getCount function. And set it prop into state. Than i try set it in component and get infinity loop. How can i fix that?
There is code of parent component:
import React, { Component } from "react";
import Message from "./Message/Message";
export default class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
color: {
s: 30,
l: 60,
a: 1
},
counter: 0
};
}
getCount = count => this.setState(state => ({
counter: count
}));
getColor = color => {
console.log(`the color is ${color}`);
};
render() {
const counter = this.state.counter;
return (
<div>
<Message
getColor={this.getColor}
getCount={this.getCount}
color={this.state.color}
>
{undefined || `Hello World!`}
</Message>
{counter}
</div>
);
}
}
child:
import React, { Component } from "react";
export default class Message extends React.Component {
constructor(props) {
super(props);
this.changeColor = this.changeColor.bind(this);
this.state = { h: 0 };
this.counter = 0;
}
changeColor = () => {
this.setState(state => ({
h: Math.random()
}));
};
componentDidUpdate(prevProps) {
this.props.getColor(this.color);
this.props.getCount(this.counter);
}
render() {
this.counter++;
const { children } = this.props;
const { s, l, a } = this.props.color;
this.color = `hsla(${this.state.h}, ${s}%, ${l}%, ${a})`;
return (
<p
className="Message"
onClick={this.changeColor}
style={{ color: this.color }}
>
{children}
</p>
);
}
}
The problem lies in your Message component.
You are using getCount() inside your componentDidUpdate() method. This causes your parent to re-render, and in turn your Message component to re-render. Each re-render triggers another re-render and the loop never stops.
You probably want to add a check to only run the function if the props have changed. Something like:
componentDidUpdate(prevProps) {
if(prevProps.color !== this.props.color) {
this.props.getColor(this.color);
this.props.getCount(this.counter);
}
}
This will keep the functionality you need, but prevent, not only the infinity-loop, but also unnecessary updates.

compare the first state to the last state reactjs

I am trying to make an edit page. I am update the state for any changes made. I want to compare the initial state with the last state on the last save. but I can not control the first state.
export default class extends Component {
constructor(props){
super(props);
this.changeDetails = this.changeDetails.bind(this);
this.state = {
driver: this.props.driver
}
}
changeDetails = value => {
this.setState({
driver:value
})
}
onRegister = () => {
//I want to make a comparison here.
}
render() {
const {driver} = this.state
return (
<div>
<EditView driver={driver} changeDetails={this.changeDetails}/>
</div>
);
}
}
EditView.js
export default class extends Component {
render() {
const { driver} = this.props;
const changeDetails = event => {
driver['fname] = event.target.value;
this.props.changeDetails(driver);
};
return (
<div>
<Input
value={driver.fname}
onChange={event => changeDetails(event)}
/>
</div>
);
}
}
Do not mutate driver itself directly. Use something like this:
const changeDetails = event =>
this.props.changeDetails( { ...driver, fname: event.target.value } );

How to use dynamically created classes in react components

I have a class of this form:
export default class FixedMem {
constructor(totalMem){
this._totalMem = totalMem
}
get totalMem(){
return this._totalMem
}
addMem(mem){
this._totalMem += mem
}
}
I import it into my react component like this :
import Fixed from '../somewhere'
If i want to create a new classes with varying parameters based on input from a textbox and display its values. How do i call its methods from inside the render method ?. This somewhat illustrates my problem
class fixedBlock extends Component {
constructor(){
super()
this.state = {
"textInput":"",
"totalMem":0,
"fixed":null
}
}
handleInputChanged(e){
this.setState({
"textInput":e.target.value
})
}
handleButtonPressed(){
this.setState({"fixed":new Fixed(parseInt(this.state.textInput))})
}
incrementButtonPressed(){
this.state.fixed.addMem(2)
}
render(){
return(
<div>
<input type="button" onClick={this.handleInputChanged} value=
{this.state.textInput}>
<button onClick={this.handleButtonPressed}>create</button>
<button onClick={this.incrementButtonPressed}> increment </button>
<p>{this.state.fixed.totalMem}</p>
</div>
)
}
}
this doesn't work, another approach i had to solve this problem was using closures, so inside my react component :
class fixedBlock extends Component{
constructor(){//stuff here}
FixedMem () {
var FixedObj = null
return {
initFixed: function (totalMem) {
FixedObj = new Fixed(totalMem, divisions)
},
totalMem: function () {
return FixedObj.totalMem
},
increment: function(){
FixedObj.addMem(2)
}
render(){//stuff here}
}
How do i even use this in the render method ?
There are several issues with your code example. Missing closing tags and rebinding of methods missing.
Here's an example of dynamically usage of a class instance in a React component. However I can not recommend to use this approach. This is mainly as proof of concept.
class MyValue {
constructor(val) {
this._val = parseInt(val, 10) || 0;
}
get total() {
return this._val;
}
set total(val) {
this.val = val;
}
add(val) {
this._val += val;
}
subtract(val) {
this._val -= val;
}
}
Here's the React component
class Block extends React.Component {
constructor() {
super();
this.state = {
textInput: "",
myValue: new MyValue()
};
}
handleInputChanged(e) {
this.setState({
textInput: e.target.value
});
}
handleButtonPressed() {
this.setState({ myValue: new MyValue(this.state.textInput) });
}
incrementButtonPressed() {
this.state.myValue.add(2);
this.forceUpdate(); /* React does not know the state has updated, force update */
}
render() {
return (
<div>
<input type="number" step="1" onChange={this.handleInputChanged.bind(this)} />
<button onClick={this.handleButtonPressed.bind(this)}>create</button>
<button onClick={this.incrementButtonPressed.bind(this)}>increment</button>
<p>{this.state.myValue.total}</p>
</div>
);
}
}
As an alternative approach. You could use a pattern where you separate logic from presentation. Here's an example using function as child. The Calculator handles the calculation and Presentation uses the calculator and present the GUI.
class Calculator extends React.Component {
constructor() {
super();
this.state = {value: 0};
}
add(value){
this.setState(prevState => ({value: prevState.value + value}));
}
subtract(value){
this.setState(prevState => ({value: prevState.value - value}));
}
set(){
this.setState(prevState => ({value: parseInt(prevState.input, 10) || 0}));
}
input(value){
this.setState({input: value});
}
render() {
return this.props.children(
{
value: this.state.value,
add: this.add.bind(this),
subtract: this.subtract.bind(this),
set: this.set.bind(this),
input: this.input.bind(this),
});
}
}
const Presentation = props => (
<Calculator>
{ ({value,add,subtract,set,input}) => (
<div>
<button onClick={() => add(2)}>add 2</button>
<button onClick={() => subtract(3)}>subtract 3</button>
<input type="number" step="1" onChange={e => input(e.target.value)} />
<button onClick={set}>set</button>
<p>{value}</p>
</div>)
}
</Calculator>);
The problem with the first attempt is that you are mutating a Component's state without letting React know about it. You need to use setState() or forceUpdate(). One way to still have FixedMem manage your state while letting React know could be:
class FixedBlock extends Component {
constructor(props) {
super(props);
this.state = {
textInput: '',
totalMem: 0
};
this.fixedMem = new FixedMem(0);
this.sync = this.sync.bind(this);
}
sync() {
const totalMem = this.fixedMem.totalMem;
this.setState({ totalMem });
}
handleInputChanged(evt) {
this.setState({ textInput: evt.target.value });
}
handleButtonPressed() {
this.fixedMem = new FixedMem(parseInt(this.state.textInput));
this.sync();
}
incrementButtonPressed() {
this.fixedMem.addMem(2);
this.sync();
}
render() {
return (
<div>
<input type="text" onChange={this.handleInputChanged.bind(this)} />
<button onClick={this.handleButtonPressed.bind(this)}>create</button>
<button onClick={this.incrementButtonPressed.bind(this)}>increment</button>
<p>{this.state.totalMem}</p>
</div>
);
}
}

Incrementing state value by one using React

In React I am trying to make a button increment a value stored in state.
However using the code below function my value is set undefined or NaN when using handleClick.
class QuestionList extends React.Component {
constructor(props) {
super(props);
this.state = {value: 0};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick = (prevState) => {
this.setState({value: prevState.value + 1});
console.log(this.state.value)
}
Can you tell me why this is happening? it should be correct according to the docs here:
https://facebook.github.io/react/docs/state-and-lifecycle.html
Because you are using the handleClick function incorrectly. Here:
handleClick = (prevState) => { .... }
prevState will be an event object passed to handleClick function, you need to use prevState with setState, like this:
handleClick = () => {
this.setState(prevState => {
return {count: prevState.count + 1}
})
}
Another issue is, setState is async so console.log(this.state.value) will not print the updated state value, you need to use callback function with setState.
Check more details about async behaviour of setState and how to check updated value.
Check the working solution:
class App extends React.Component {
constructor(props){
super(props);
this.state={ count: 1}
}
onclick(type){
this.setState(prevState => {
return {count: type == 'add' ? prevState.count + 1: prevState.count - 1}
});
}
render() {
return (
<div>
Count: {this.state.count}
<br/>
<div style={{marginTop: '100px'}}/>
<input type='button' onClick={this.onclick.bind(this, 'add')} value='Inc'/>
<input type='button' onClick={this.onclick.bind(this, 'sub')} value='Dec'/>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
<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='container'></div>
set state is async so you wont see the value update when the console.log happens. You should have the state value printed out on the UI so you can see whats happening. To fix the console log try this.
class QuestionList extends React.Component {
constructor(props) {
super(props);
this.state = {value: 0};
}
handleClick = (prevState) => {
this.setState({value: prevState.value + 1}, () => {
console.log(this.state.value)
});
}
NOTE: when you define an inline lambda (arrow function) for a react class this is bound correctly so you dont need to bind it in the constructor.
also you can change the way you pass the previous number if its just a state increment like this
handleClick = () => {
this.setState({value: this.state.value + 1}, () => {
console.log(this.state.value)
});
}
Hello there, try these codes to increment your value
class Counter extends React.Component{
constructor(props){
super(props);
this.addOne = this.addOne.bind(this);
this.state = {
count : 0
}
}
addOne() { // addOne as HandleClick
this.setState((preState) => {
return {
count : preState.count + 1
};
});
}
render() {
return (
<div>
<h1>Count : {this.state.count}</h1>
<button onClick={this.addOne}>+1</button>
</div>
);
}
}
ReactDOM.render(<Counter />, document.getElementById('YOUR-ID'));
class SkuVariantList extends React.Component {
constructor(props) {
super(props)
this.state = {
clicks: 0
};
this.clickHandler = this.clickHandler.bind(this)
}
componentDidMount() {
this.refs.myComponentDiv.addEventListener('click', this.clickHandler);
}
componentWillUnmount() {
//this.refs.myComponentDiv.removeEventListener('click', this.clickHandler);
}
clickHandler() {
var clk = this.state.clicks
this.setState({
clicks: clk + 1
});
}
render() {
let children = this.props.children;
return (
<div className="my-component" ref="myComponentDiv">
<h2>My Component ({this.state.clicks} clicks})</h2>
<h3>{this.props.headerText}</h3>
{children}
</div>
);
}
}
Try this out
class QuestionList extends React.component {
constructor(props){
super(props)
this.state = {
value : 0
}
}
handleClick(){
this.setState({
value : this.state.value + 1
})
}
render(){
return( <button type="button" onClick={this.handleClick.bind(this)}> {this.state.value} </button> )
}
}
Note that when you set a state, it triggers the render function, which will reflect the current state. Try it out in the browser!
import React from 'react'
class App extends React.Component{
constructor(){
super()
this.state = {
count: 0
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(){
this.setState(prevState => {
return {
count: prevState.count + 1
}
})
}
render(){
return(
<div style = {{display: 'flex', fontSize: 30, flexDirection: 'column', alignItems:'center'}}>
<h1>{this.state.count}</h1>
<button onClick = {this.handleClick}>Change</button>
</div>
)
}
}
export default App
This is the shortest code for that. First, initialize the state, then perform a method to increment.
state = {
counter: 0
}
increaseHandler = () => {
let counter = this.state.counter
counter += 1
this.setState({counter: counter})
}
You can do it this way also where we do both increment and decrement operation with same function making it more modular and redable
class CounterApp extends React.Component{
constructor(){
super();
//here count is initially assigned with 0
this.state ={
count:0
}
}
//when we click Increment or Decrement +1 or -1 is passed to step and the value gets changed it gets updated to the view
increment = (step) =>{
this.setState({
count:this.state.count + step
})
}
render(){
const { count } = this.state;//same as const count = this.state.count;
return(
<div>
<div className="counter-app">
<h2 className="value">{count}</h2>
<button onClick={() => this.increment(+1)}>Increment</button>
<button onClick={() => this.increment(-1)}>Decrement</button>
</div>
</div>
)
}
}

Resources