Passing Function Down [React] - reactjs

I'm trying to pass down a function called handleDeleteToDoItem from the parent ToDoContainer to the child ToDoListOfItems.
This is done with <ToDoListOfItems toDoItems={this.state.toDoItems} deleteToDoItem={this.handleDeleteToDoItem} />
However, I'm getting an error that the function is never received by the child class when I reference it with this.props.deleteToDoItem when I'm rendering ToDoItems inside of ToDoListOfItems
All of the other states which I've passed down from ToDoContainer are being recognized, except for deleteToDoItem and I'm at a loss of what I've done wrong here. My IDE (Webstorm) is telling me the variable is unresolved.
Is there a different way I should be passing down functions from parent components to child components?
Thanks,
import React, {Component} from 'react';
import './App.css';
class ToDoContainer extends Component {
constructor(props) {
super(props);
this.state = {
toDoItems: [],
toDoWhat: ""
};
...
this.handleDeleteToDoItem = this.handleDeleteToDoItem.bind(this);
}
handleDeleteToDoItem(uniqueID) {
let updatedItemList = this.state.toDoItems.filter(toDo => {
return toDo.uniqueID !== uniqueID;
});
// Concat updated item list to blank array
this.setState({
toDoItems: [].concat(updatedItemList)
})
}
render() {
return (
<div>
<div>
<div>
<ToDoListOfItems toDoItems={this.state.toDoItems} deleteToDoItem={this.handleDeleteToDoItem} />
</div>
</div>
{/* TODO Create a form with a submit button to add items to the todolist */}
<form action="">
<div>
{/* Capture the value of the form input */}
<input type="text" onChange={this.handleChangeToDoItem} value={this.state.toDoWhat}/>
</div>
<div>
<button onClick={this.handleAddToDoItem}>Add Item to List</button>
</div>
</form>
</div>
);
}
}
// This is the container for all the indivdual items in the to-do list
class ToDoListOfItems extends Component {
render() {
//TODO Put in styling for the list of items
// Use map() to iterate through each item in the to-do list, creating new elements in the list container
return (
<ul>
{this.props.toDoItems.map(toDo => (
<ToDoItem key={toDo.uniqueID}
id={toDo.uniqueID}
toDoWhat={toDo.toDoWhat}
completed={toDo.isDone}
onDelete={this.props.deleteToDoItem}/>
))}
</ul>
)
}
}

Its a key name mismatch issue, because in ToDoItem you are passing the function by key onDelete not by deleteToDoItem:
Here:
<ToDoItem key={toDo.uniqueID}
id={toDo.uniqueID}
toDoWhat={toDo.toDoWhat}
completed={toDo.isDone}
onDelete={this.props.deleteToDoItem} // here
/>
So inside ToDoItem component it will be available by this.props.onDelete.
Suggestion: To avoid the confusion use key deleteToDoItem at all the places.

Try making the deleteToDoItem prop in the container class equal to an arrow function calling the handleDeleteToDoItem with parameters:
... deleteToDoItem={uniqueID => this.handleDeleteToDoItem(uniqueID)}
Also, using an arrow function in this way makes it so you don't need to explicitly bind the handler function. Make it into an arrow function (above) and it is automatically bound.

Related

How to check if all instances of a React Component have the same value for their state?

So right now, in my App file I have this:
{items.map(el => (
<Item
prop1={foo}
prop2={bar}
el={baz}
/>
))}
And in the <Item> component, I have this:
<span className={finishedClass ? "finishedItem" : ""}>
{props.el}
</span>
where finishedClass is a state variable.
So my question is, how can I check if finishedClass is true for every single <Item> component that gets generated as a result of the items.map?
So, you basically want to know if all the finishedClass state values in all the Item components are true. This can be simplified in the sense that if any of the Item components have finishedClass as false, you will perform a certain action.
So, what you can do is, pass a function as a prop to the Item component as follows:
{items.map(el => (
<Item
prop1={foo}
prop2={bar}
el={baz}
setFinishedClassToFalse={()=>{/*your statements*/}}
/>
))}
This function setFinishedClassToFalse will be called by the Item component if and only if its state value finishedClass is false. Obviously, there's a little more implementation to this than what I have described. But this should give you a start.
Parent component can communication with child component with parent props that have function in it. You can see code below onFinished property in Parent component has handleFinished() function value in it. That fucntion will be a bridge to help Child component to communicate with Parent component. On the Child component you must run onFinished props to triggering handleFinished function on Parent comoponent. In code below the trigger for props.onFinished is when <span> clicked by user.
import React from "react";
import ReactDOM from "react-dom";
import { Grid } from "react-flexbox-grid";
class App extends React.Component {
constructor(props) {
super(props);
this.state = { data: "test" };
}
render() {
const items = ["red", "green", "blue"];
const handleFinished = (e, index) => {
this.setState({ data: e });
console.log(this.state);
};
return (
<Grid>
{items.map((el, index) => (
<Child
prop1={"test"}
prop2={"test1"}
el={el}
onFinished={(e) => handleFinished(e, index)}
/>
))}
<div>{this.state.data.toString()}</div>
</Grid>
);
}
}
const Child = (props) => {
// example for your finishedClass value state
const finishedClass = true;
return (
<span
onClick={() => props.onFinished(finishedClass)}
className={finishedClass ? "finishedItem" : ""}
>
{props.el}{" "}
</span>
);
};
ReactDOM.render(<App />, document.getElementById("container"));
As finishedClass is a state variable, if you just console.log(finishedClass) inside component it will just do the work

Is it possible to render a react class when given its name as a string?

Here's a smaller example of what I'm trying to do, I don't know if it's possible to do something similar or I should use an entirely different method.
import {Design1, Design2} from './page-designs';
let designs = {
"page1":"Design1",
"page2":"Design2",
"page3":"Design1",
"page4":"Design2"
}
class DesignedPage extends React.Component {
let Design = designs[this.props.page]
render(){
return(
<div className="row flex-fill d-flex">
<div className="col-1"></div>
<Design /* This is the line that fails */
data = {this.props.data}
/>
</div>
</div>
)}
}
class Main extends React.Component {
render(){
return(
<DesignedPage
page = {this.props.openPage} /*this could be any of page1-4 depending on button a click*/
data = {this.props.data}
/>
)}
}
Ideally this would render the react elements Design1 or Design2 based on what props.page is passed, but instead it returns
"Warning: <Design1 /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements." and "The tag <Design1> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter."
I've thought of making a long if, elseif, elseif.. statement in DesignedPage (the actual code has many more than 2 designs), which I'm fairly confident would work, but looks very messy in comparison.
You can't render the component name by getting its name as a string. You need to map the string to the component iteself:
let designs = {
"page1":Design1,
"page2":Design2,
}
If you pass a string, react would think it's a HTML tag, hence it say'Design1' tag is unrecognised. Also, you could import the components and use them as values in the designs object in place of strings.
let designs = {
"page1":Design1,
"page2":Design2,
"page3":Design1,
"page4":Design2
}
make one function that return react component..
getComponent = ({data, pageName}) => {
if(pageName === "page1") return <Desig1 />;
if(pageName === "page2") return <Design2 />;
}
and call function from render of DesignedPage component
const {page, data} = this.props;
return(
<div className="row flex-fill d-flex">
<div className="col-1">
{getComponent(page, data)}
</div>
</div>
)

In React, how do I pass arguments into a parent component's handleClick function?

In the main component's render method, I have this line,
<BettingChips onClick={(betAmount) => this.handleClick(betAmount)} />
Which corresponds to this function,
handleClick(betAmount){
alert(betAmount);
}
However under the child component, I can't seem to pass arguments to the handleClick function,
class BettingChips extends Component{
render(){
return(
<div>
<button onClick={this.props.onClick} value={1} >1</button>
// ... etc
</div>
)
};
I can write this.props.onClick... But I can't write this.props.onClick(1) so that it passes in the integer value of 1 to the parent component. How do I work around that? Thanks.
It's quite simple. Just change
<button onClick={this.props.onClick} value={1} >1</button>
to
<button onClick={() => this.props.onClick(1)} value={1} >1</button>
Cheers!
Try to do this without using arrow function definition inside props (bad for child components performance, as will always be a new function reference more info here)
Parent class does not need the arrow function defined when passing onClick to BettingChips, we can do this at the class level instead, using arrow function to lexically bind this...
class SomeParentComponent extends Component {
handleClick = (betAmount) => {
alert(betAmount)
}
render() {
return <BettingChips onClick={this.handleClick} />
}
}
In BettingChips use event to extract the value from the target of the event (the button that was clicked), so you don't need to use arrow function with amount as parameter
class BettingChips extends Component {
onClick = (e) => {
this.props.onClick(e.target.value)
}
render() {
return(
<div>
<button onClick={this.onClick} value={1}>1</button>
// ... etc
</div>
)
}
}
This must work for you:
class BettingChips extends Component{
render(){
return(
<div>
<button onClick={() => this.props.onClick(1)} value={1} >1</button>
// ... etc
</div>
)
};
One way could be to create a new function in your child component and call your parent function from props there. This is useful if you need to perform some more complicated logic when the button is clicked. Here is a code example:
class BettingChips extends Component{
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
// do something else or prepare parameters for parent's `onClick`
this.props.onClick(1);
}
render(){
return(
<div>
<button onClick={this.onClick} value={1}>1</button>
</div>
);
};

Can't get button component value onClick

I'm sure this is something trivial but I can't seem to figure out how to access the value of my button when the user clicks the button. When the page loads my list of buttons renders correctly with the unique values. When I click one of the buttons the function fires, however, the value returns undefined. Can someone show me what I'm doing wrong here?
Path: TestPage.jsx
import MyList from '../../components/MyList';
export default class TestPage extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleButtonClick = this.handleButtonClick.bind(this);
}
handleButtonClick(event) {
event.preventDefault();
console.log("button click", event.target.value);
}
render() {
return (
<div>
{this.props.lists.map((list) => (
<div key={list._id}>
<MyList
listCollection={list}
handleButtonClick={this.handleButtonClick}
/>
</div>
))}
</div>
);
}
}
Path: MyListComponent
const MyList = (props) => (
<div>
<Button onClick={props.handleButtonClick} value={props.listCollection._id}>{props.listCollection.title}</Button>
</div>
);
event.target.value is for getting values of HTML elements (like the content of an input box), not getting a React component's props. If would be easier if you just passed that value straight in:
handleButtonClick(value) {
console.log(value);
}
<Button onClick={() => props.handleButtonClick(props.listCollection._id)}>
{props.listCollection.title}
</Button>
It seems that you are not using the default button but instead some sort of customized component from another libray named Button.. if its a customezied component it wont work the same as the internatls might contain a button to render but when you are referencing the event you are doing it throug the Button component

i am not able retrieve array elements one at a time if i call them in my component all the elements are retrieved at a time

I want to load my array element when an event is occurred by referencing the key i tried different variables for the key but it would not accept all the elements of the array are being displayed if i give index as the key.
I am new to Reactjs and not very familiar with all the syntax and concept can somebody help me with the logic to solve this.
The event I am triggering is onClick or onChange.
`var Qstn =['A','B','C','D','E','F','G','H','I','J'];
<div>
{Qstn.map(function(Q,index){
return <span className="col-md-4 txt" key={index}>{Q}</span>
})}
</div>`
Ok I made a codepen with an example
http://codepen.io/lucaskatayama/pen/QGGwKR
It's using ES6 classes components, but it's easy to translate.
You need to set initial state to an empty array like [].
On click button, it call onClick() method which uses this.setState({}) to change component state.
When React notice state changes, it re-render the component.
class Hello extends React.Component {
constructor(){
super();
//Initial State
this.state = {
Qstn : []
}
}
onClick(){
//Called on click button
// Set state to whatever you want
this.setState({Qstn : ['A','B','C','D','E','F','G','H','I','J']})
}
render(){
let Qstn = this.state.Qstn; // load state and render
return (
<div>
<button onClick={() => this.onClick()}>Click</button>
<div>
{Qstn.map(function(Q,index){
return <span className="col-md-4 txt" key={index}>{Q}</span>
})}
</div>
</div>
)
}
}
ReactDOM.render(<Hello />, document.getElementById('container'))

Resources