Child Component does not refresh when List Array is updated - reactjs

I am new to React and I've ran into a problem where I am updating an array list in my app component from a function that is called from user input. This is working fine but there is a child component that is get's the content of that list and maps it to buttons that it displays. My problem is then the list is updated in in the app component it the child component dose not update.
This is the code for my app component:
import React from 'react';
import './App.scss';
import DiceSelector from "./components/DiceSelector";
import ListOfDiceTypes from "./shared/list-available-dice";
const DiceToRollList = [];
const RolledTotal = 45;
DiceToRollList.push(ListOfDiceTypes[4]);
DiceToRollList.push(ListOfDiceTypes[4]);
DiceToRollList.push(ListOfDiceTypes[3]);
export default class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="App">
<header className="App-header">
<div className="Dice-Box">
<h2>Select Dice to Roll</h2>
{ListOfDiceTypes.map((dice, index) => (
<DiceSelector
key={"typeList" + index}
dicetype={dice.dicetype}
imagename={dice.imageName}
maxvalue={dice.maxvalue}
onClick={AddDieClick}
/>
))}
</div>
<div className="Dice-Box">
<h2>Selected Dice</h2>
{DiceToRollList.map((dice, index) => (
<DiceSelector
key={"SelectedList" + index}
dicetype={dice.dicetype}
imagename={dice.imageName}
maxvalue={dice.maxvalue}
onClick={RemoveDieClick}
/>
))}
<h3>Total Rolled = {RolledTotal}</h3>
</div>
</header>
</div>
);
}
}
And here is my child component:
import React from "react";
export default class DiceSelector extends React.Component {
constructor(props) {
super(props);
}
render() {
return <button
className="number"
onClick={() => this.props.onClick(this.props.dicetype)}
>
<img src={require(`../images/${this.props.imagename}`)} alt={this.props.imagename} />
</button>
}
}

You can store key in state of App component and use different key each time when you update props that you are going to pass child component at
<DiceSelector
key= { this.state.key+ offset + index} // insted of {"typeList" + index}
dicetype={dice.dicetype}
imagename={dice.imageName}
maxvalue={dice.maxvalue}
onClick={AddDieClick} // or onClick={RemoveDieClick}
/>
// offset is used to escape duplicate elements next time when child component will render
// offset is the length of the array on that you are mapping
You can add logic of update of key in clickHandler methods
This will rerender the child component.
Or you can use componentWillReceiveProps method to update the state of child component when props changes.

I had a couple of things wrong here. I realized that I did not need the child to refresh when I updated the list I needed the app component to refresh. Also I had functions outside of the app class declared as function functionName(){} rather than in the class as functionName = () = {}. This was keeping me from properly accessing this.state. Finally I was binding to the DiceToRollList rather than this.sate.DiceToRollList. Here is how I updated the app component.
import React, { Component } from 'react';
import './App.scss';
import DiceSelector from "./components/DiceSelector";
import ListOfDiceTypes from "./shared/list-available-dice";
const DiceToRollList = [];
const RolledTotal = 45;
DiceToRollList.push(ListOfDiceTypes[4]);
DiceToRollList.push(ListOfDiceTypes[4]);
DiceToRollList.push(ListOfDiceTypes[3]);
const offset1 = 100;
const offset2 = 500;
export default class App extends Component {
constructor(props) {
super(props);
this.state = { DiceToRollList }
}
render() {
return (
<div className="App">
<header className="App-header">
<div className="Dice-Box">
<h2>Select Dice to Roll</h2>
{ListOfDiceTypes.map((dice, index) => (
<DiceSelector
key={ListOfDiceTypes.length + index} // insted of {"typeList" + index}
dicetype={dice.dicetype}
imagename={dice.imageName}
maxvalue={dice.maxvalue}
onClick={this.AddDieClick}
/>
))}
</div>
<div className="Dice-Box">
<h2>Selected Dice</h2>
{this.state.DiceToRollList.map((dice, index) => (
<DiceSelector
key={this.state.DiceToRollList.length + offset2 + index} // insted of {"dieToRollList" + index}
dicetype={dice.dicetype}
imagename={dice.imageName}
maxvalue={dice.maxvalue}
onClick={this.RemoveDieClick}
/>
))}
<h3>Total Rolled = {RolledTotal}</h3>
</div>
</header>
</div>
);
}
// Functions
AddDieClick = (diceType) => {
let RowToAdd = ListOfDiceTypes.findIndex(obj => {
return obj.dicetype === diceType
});
DiceToRollList.push(ListOfDiceTypes[RowToAdd]);
this.reSetState();
}
reSetState = () => {
this.setState({ DiceToRollList });
};
RemoveDieClick = (diceType) => {
let DieToAdd = DiceToRollList.findIndex(obj => {
return obj.dicetype === diceType
});
DiceToRollList.splice(DiceToRollList[DieToAdd], 1);
this.reSetState();
}
}

Related

React - Passing data counter from child to parents component

so, im just learning about props method and i want to try how to passing data from child to parent component.
im trying to make counter app, when click + the counter increasing and click - the counter decreasing
i have parent component like this
import React, { useState } from 'react';
import ComponentB from './ComponentB'
const ComponentA = () => {
const [counterB , setCounterB] = useState(0);
return(
<div>
<h1>ComponentB</h1>
<h3>{counterB}</h3>
<ComponentB
add={counterB => setCounterB(counterB)}
subtract={counterB => setCounterB(counterB)}/>
</div> );
}
export default ComponentA;
and child component like this
import React from 'react';
const ComponentB = (props) =>{
return(
<div>
<button onClick={()=> props.add(+1)}>+</button>
<button onClick={()=> props.subtract(-1)}>-</button>
</div>
);
}
export default ComponentB
With
return(
<div>
<h1>ComponentA</h1>
<h3>{counterB}</h3>
<ComponentB
add={diff => setCounterB(counterB + diff)}
subtract={diff => setCounterB(counterB + diff)}/>
</div> );
you pass the function diff => setCounterB(counterB + diff) to ComponentB as prop.add. This function sets the state of counterB to the first argument of that function. So when the button invokes props.add(+1)} the value of counterB is set to 1 every time it is pressed.
What you want is to add +1 to the state of counterB. Thus you should it this way:
// Get a hook function - only needed for this Stack Snippet
const {useState} = React;
const ComponentA = () => {
const [counterB , setCounterB] = useState(0);
return(
<div>
<h1>ComponentA</h1>
<h3>{counterB}</h3>
<ComponentB
add={diff => setCounterB(counterB + diff)}
subtract={diff => setCounterB(counterB + diff)}/>
</div> );
}
const ComponentB = (props) =>{
return(
<div>
<button onClick={()=> props.add(+1)}>+</button>
<button onClick={()=> props.subtract(-1)}>-</button>
</div>
);
}
ReactDOM.render(
<ComponentA/>,
document.getElementById("react")
);
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I'd suggest to create a method in the Parent component to update the state given a value received from the child.
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
this.updateCountWithValue = this.updateCountWithValue.bind(this);
}
updateCountWithValue(value) {
let count = this.state.count;
count += value;
this.setState({
count: count
});
}
render() {
return (
<div>
<h1>{`Count: ${this.state.count}`}</h1>
<Child updateCount={this.updateCountWithValue} value={1} />
<Child updateCount={this.updateCountWithValue} value={-1} />
</div>
);
}
}
class Child extends Component {
render() {
return (
<div>
<button onClick={() => this.props.updateCount(this.props.value)}>
{this.props.value}
</button>
</div>
);
}
}
As you can see, the props of my Child component are:
The value that the child will update or change
A method to update the Parent component.
I made this sample so you can see how it works: https://codesandbox.io/s/child-to-parent-uvfmi?file=/src/App.js
Instead of this:
<ComponentB
add={counterB => setCounterB(counterB)}
subtract={counterB => setCounterB(counterB)}/>
Try this:
<ComponentB
add={val => setCounterB(counterB+val)}
subtract={val => setCounterB(counterB+val)}/> // using '+' coz val = -1

React: Onclick Event new component is not returned

I am new to React. I am trying to display new component in the content on click of menu from Navbar. But the return of component on click function doesnot work. So return doesnot work. Here are my files. Click function "getItemsData" works but the component is not returned. I am trying to create a application with restaurant menu and it relevant content
App.jsx
import React from 'react';
import "./style.css";
import Slider from "./carousel.js"
class App extends React.Component{
render(){
return(
<div>
<Header/>
</div>
);
}
}
class Header extends React.Component{
constructor(props){
super(props);
this.state = {
sections: [],
menu: []
};
}
componentDidMount(){
fetch('http://localhost:3001/api/sections')
.then(res => res.json())
.then(sections => this.setState({ 'sections': sections }))
fetch('http://localhost:3001/api/menu')
.then(res => res.json())
.then(menu => this.setState({ 'menu': menu }))
}
render(){
return(
<div id="header-bar">
<div id="header-logo">
<img src={require('./images/1200px-Burger_King_Logo.png')} alt="Logo" id="logo-class"></img>
</div>
<Slider sections={this.state.sections} menu={this.state.menu} />
</div>
);
}
}
export default App;
carousel.js
import React , { Component } from 'react';
import contentCards from "./contentcards.js";
const Slider = (data) => {
console.log(data)
var Menuoptions = data.menu.options
var Sectionsref = data.sections
const getItemsData = (sectionData) => {
sessionStorage.setItem("sectionData" , JSON.stringify(sectionData));
return <contentCards />;
}
return(
<div className="sectionNavBar">
{(()=> {
if (Menuoptions !== undefined && Sectionsref !== undefined) {
if (Menuoptions.length > 0 && Sectionsref.length > 0){
let rowmenu = []
for(let i=0; i< Menuoptions.length ; i++){
for(let j=0; j<Sectionsref.length; j++){
if(Menuoptions[i]._ref === Sectionsref[j]._id){
// console.log(Menuoptions[i]._ref+ "==" +Sectionsref[j]._id)
let imageId = Sectionsref[j].carouselImage.asset._ref;
imageId = imageId.slice(6)
imageId = imageId.replace("-png", ".png")
//console.log(Sectionsref[j])
rowmenu.push(<div key={j} className="listNavBar" onClick = {() => getItemsData(Sectionsref[j])}> <img src={window.location.origin + '/images/'+imageId} className= "navBar-image" /> <br/>{Sectionsref[j].name.en} </div>)
}
}
}
return rowmenu
}
}
})()}
</div>
)
}
}export default Slider
contentCards.js
import React , { Component } from 'react';
class contentCards extends React.Component{
render(){
return (
<div className = "cardColumn"> Menu1 </div>
)
}
}
export default contentCards
Custom components names must be uppercased or React will treat them as DOM elements.
React treats components starting with lowercase letters as DOM tags.
// not <contentCards />
<ContentCards />

How to pass the data from child to parent? when parent is class based and child is functional based

I want to pass the data which is anything like, array, strings, numbers into the App(Parent) Component) from the Child1(Child) components.
There's a parent who is class-based and the child is functional based.
Parent:
import React, { Component } from "react";
import "./App.css";
import Child1 from "./Child1/Child1";
class App extends Component {
state = { message: "" };
callbackFunction = (event) => {
this.setState({
message: event.target.value
});
}
render() {
return (
<div className="App">
<Child1 />
</div>
);
}
}
export default App;
Child
import React from 'react'
const Child1 = (props) => {
return (
<div>
</div>
);
}
export default Child1;
If you want to send some data to the parent component upon clicking a button that's in the child component, you can do it this way:
import React from 'react'
const Child1 = React.memo((props) => {
return (
<div>
<button onClick={() => {props.clicked('some data')}} type="button">Click me to send data<button>
</div>
);
})
export default Child1;
now you can assign a function to the clicked prop in your parent component and that gets called when the button in the child component is clicked:
class App extends Component {
state = { message: "" };
callbackFunction = (event) => {
this.setState({
message: event.target.value
});
}
clicked = (data) => { // this func. will be executed
console.log(data);
}
render() {
return (
<div className="App">
<Child1 clicked={this.clicked}/>
</div>
);
}
}
I have founded another solution for you using combine Functional-Class-functional component pass data. here the live code for https://codesandbox.io Click and check that.
Also same code added bellow here:
app component
import React, { useState } from "react";
import "./styles.css";
import Child from "./child";
export default function App() {
const [name, setName] = useState("Orignal Button");
const [count, setCount] = useState(0);
const handleClick = _name => {
setName(_name);
setCount(count + 1);
};
const resetClick = e => {
setName("Orignal Button");
setCount(0);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Child
name={name + " - " + count}
handleClick={handleClick}
resetClick={resetClick}
/>
</div>
);
}
child1 component
import React from "react";
import Child2 from "./child2";
class Child extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<>
<h2>This is child</h2>
<button onClick={e => this.props.handleClick("New Name changed")}>
{this.props.name}
</button>
<Child2 handleChilc2Click={this.props.resetClick} />
</>
);
}
}
export default Child;
Another component Child2
import React from "react";
const Child2 = props => {
return (
<>
<h2>Child 2</h2>
<button onClick={e => props.handleChilc2Click(e)}>
Click me for reset Parent default
</button>
</>
);
};
export default Child2;
You can some help from it.
Thanks
You can pass a function to Child (called Counter in the example) that will change Parent state. Since you pass the same reference for this function every time (this.increment) the only Children that will render are those that changed.
const Counter = React.memo(
//use React.memo to create pure component
function Counter({ counter, increment }) {
console.log('rendering:', counter.id)
// can you gues why prop={new reference} is not a problem here
// this won't re render if props didn't
// change because it's a pure component
return (
<div>
<div>counter is {counter.count}</div>
<button onClick={() => increment(counter.id)}>
+
</button>
</div>
)
}
)
class Parent extends React.PureComponent {
state = {
counters: [
{ id: 1, count: 0 },
{ id: 2, count: 0 }
]
}
increment = _id =>
this.setState({
counters: this.state.counters.map(val =>
_id === val.id
? { ...val, count: val.count + 1 }
: val
)
})
render() {
return (
<div>
{this.state.counters.map(counter => (
<Counter
counter={counter}
increment={this.increment}
key={counter.id}
/>
))}
</div>
)
}
}
//render app
ReactDOM.render(<Parent />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

How to render one Component on button click in another Component

I am new to React and am having trouble wrapping my head around props/states.
So I have component SortingVisualizer, that generates a visual representation of an unsorted array as follows:
class SortingVisualizer extends React.Component {
constructor(props){
super(props);
this.state = {
array: [],
};
}
componentDidMount(){
this.resetArray();
}
resetArray(){
const array = [];
for(let i = 0; i<100; i++){
array.push(this.getRandomInt(1,500))
}
this.setState({array});
}
//Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
render(){
const {array} = this.state;
return(
<div className="array-container">
{array.map((value, idx) => (
<div className = "array-elem" key = {idx} style = {{height: `${value}px`}}>
</div>
))}
</div>
);
}
}
export default SortingVisualizer;
Now, I have a Navbar Component with a button "Generate new Array" :
class Navbar extends React.Component {
render(){
return(
<nav className = "navbar">
<button className = "new-array-btn" onClick ={this.props.GNAclick}>Generate New Array</button>
</nav>
)
}
}
export default Navbar;
What I want to achieve is that on button click, resetArray will be called on SortingVisualizer so a new array will be generated.
Here is my App.js:
class App extends React.Component {
GNAclickHandler = () => {
console.log("clicked!");
/*
this.setState((prevState) => {
return {GNA: !prevState.GNA}
});
*/
}
render() {
return (
<>
<header>
<Navbar GNAclick = {this.GNAclickHandler}/>
</header>
<div className="App">
<SortingVisualizer />
</div>
</>
);
}
}
export default App;
I am not sure how to progress from here, any help will be appreciated.
Here is the website: https://roy-05.github.io/sort-visualizer/
I would recommend you to read this article https://reactjs.org/docs/thinking-in-react.html, so you can have a better understanding of state and props.
Regarding to your app, you just need to know which component has state and which will react to props change.
App.js
// Navbar manages the state
// and provides it to their children
// so they can rerender whenever the app state changes
class App extends React.Component {
state = {
data: Date.now()
}
createNewArray = () => {
this.setState({
data: Date.now()
})
}
render() {
return (
<React.Fragment>
<Navbar onGenerate={this.createNewArray}/>
<SortingVisualizer data={this.state.data}/>
</React.Fragment>
)
}
}
Navbar.js
// Navbar just notifies that
// and action has to be executed
// (the parent handles that action by creating a new set of data)
function Navbar(props) {
return (
<nav>
<button onClick={props.onGenerate}>Generate array</button>
</nav>
)
}
SortingVisualizer.js
// SortingVisualizer renders what App tells it to render
function SortingVisualizer(props) {
return (
<main>
{props.data}
</main>
);
}

Passing method child's component to another 'external' component in ReactJS

I'm new to ReactJs, coding and this is my first time posting here! So, I'm trying to build a Todo app in ReactJs. I have four components.
the first compo. is App.js - the parent one
import React, { Component } from 'react';
import TaskTodo from './TaskTodo';
import './App.css';
import TaskDisplayed from "./TaskDisplayed";
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">Hey, i'm the header! </h1>
</header>
<div className="App-intro">
<TaskTodo/>
</div>
<div className="App-right">
<TaskDisplayed/>
</div>
</div>
);
}
}
export default App;
TaskTodo.js - which is the parent of the TodoItems.js
import React, {Component} from 'react';
import TodoItems from './TodoItems';
export default class TaskTodo extends Component{
constructor(props) {
super(props);
this.state = {
items: []
};
this.addItem = this.addItem.bind(this);
};
addItem(e) {
const itemArray = this.state.items;
if (this._inputElement.value !== "") {
itemArray.unshift(
{
text: this._inputElement.value,
key: Date.now()
}
);
this.setState({
items: itemArray
});
this._inputElement.value = "";
}
e.preventDefault();
}
render() {
return (
<div className="todoListMain">
<div className="header">
<form onSubmit={this.addItem}>
<input type="text" ref={(a) => this._inputElement = a}
placeholder="Add a list">
</input>
</form>
</div>
<TodoItems entries={this.state.items}/>
</div>
);
}
}
TodoItems.js - the child of the TaskTodo.js
import React, { Component } from 'react';
class TodoItems extends Component {
constructor(props) {
super(props);
this.createTasks = this.createTasks.bind(this);
}
handleClick = (text) => {
console.log(text);
}
createTasks(item) {
return <li key={item.key}><a onClick={() => this.handleClick(item.key, item.text)} href={'#about'}>#{item.text}</a></li>
}
render() {
const todoEntries = this.props.entries;
const listItems = todoEntries.map(this.createTasks);
return (
<ul className="theList">
{listItems}
</ul>
);
}
};
export default TodoItems;
What I need to do, is how I can pass the handleClick method (a child's of TaskTodo) to an 'external' component - TaskDisplayed.js; or how I can track when the user click to a listed item? Please pardon me for this unprofessional way of asking! But, I truly need to get in track with ReactJS! Thanks!
p.s. The above code I found online, so thanks for that :D!
You should define the onClick event handler in the parent component and pass it to the child as a prop.
See How to pass an event handler to a child component in React
In this case, you would want to define it in the App component since that is the parent of the two components that need to communicate.

Resources