How to Display Updated Array After Adding To It? - javascript

Just trying to figure this out, can't really get there...
Basically, I have something in state called "arrayCodes" that is nothing but an array of strings. I want to type in something to add in the textbox, push it to the end of the "arrayCodes", then want the updated array to display on screen. Right now, I get 'A1A2' as the output, but I want 'A1A2(userinput)'. I've put some console logs, and it has confirmed that the user input is getting added to the state, but I can't figure out how to display it on screen. Any help is greatly appreciated.
Here is the component in question:
import React, { Component } from 'react';
class Testing extends Component {
state = {
arrayCodes: ['A1', 'A2'],
currentCode: '',
}
addEditCode = (inputCode) => {
//console.log("Add Edit Code")
var arrayCode;
arrayCode = this.state.arrayCodes
console.log("array code before push", arrayCode)
arrayCode.push(inputCode)
console.log("array code after push", arrayCode)
this.setState({ arrayCodes: arrayCode })
console.log("Array of Codes is now: ", this.state.arrayCodes)
}
setCurrentCode = (input) => {
this.setState({ currentCode: input })
}
render() {
return (
<div>
<input type="text"
name="enteredCode"
placeholder="Enter an edit code to add..."
onChange={(event) =>
this.setCurrentCode(event.target.value)} />
<button onClick={() =>
this.addEditCode(this.state.currentCode)}>Click to
add</button>
<h1>Current array in state: {this.state.arrayCodes}</h1>
</div>
);
}
}
export default Testing;

You want something like this:
class Testing extends React.Component {
state = {
arrayCodes: ["A1", "A2"],
currentCode: ""
};
addEditCode = inputCode => {
const { arrayCodes } = this.state;
arrayCodes.push(inputCode);
this.setState({ arrayCodes });
};
setCurrentCode = input => {
this.setState({ currentCode: input });
};
render() {
return (
<div>
<input
type="text"
name="enteredCode"
placeholder="Enter an edit code to add..."
onChange={event => this.setCurrentCode(event.target.value)}
/>
<button onClick={() => this.addEditCode(this.state.currentCode)}>
Click to add
</button>
<h1>
Current array in state:
{this.state.arrayCodes.reduce((acc, c) => {
return acc + c;
}, "")}
</h1>
</div>
);
}
}
Working example here.

It looks like you're updating the wrong property in state. Updating editCodes array, but never reading from it. In addEditCode method, shouldn't this line:
this.setState({ editCodes: arrayCode })
be this:
this.setState({ arrayCodes: arrayCode })
?

Well the problem is in the states
editCodes ==> the one getting updated but not in the render method
arrayCodes ==>the one you are showing in the render method
currentCode ==> saving the value temporarily for the new value
Just Change it to
addEditCode = inputCode => {
let arrayCodes = this.state.arrayCodes;
arrayCodes.push(inputCode);
this.setState({
arrayCodes
});
};
Happy Coding \m/
In addition to that Use map or reduce to render the updated array

Related

React Hooks: State is resetting to empty array even if I use the spread operator, prevState, etc

Simplified Code Sample right here
WORDS:
In short: My items state is resetting to [] with each NEW checkbox clicked and I dont understand why. But instead I want to use the spread operator and useState hooks to push an new item into the array so it's an array of objects.
Current behavior in detail: I'm creating an object and setting it in state using all (and I mean ALL) manner of React useState hooks like this: setItems((prevState) => [...prevState, { [evt.target.value]: evt.target.checked }]); As I check one item it's added and items becomes an array of objects (it being added over and over again is not the problem; I'll add a check for that later). BUT Here's the problem: when I click a NEW checkbox the items array is set back to [] and isnt concatenated with the prev items—even though I'm using prevState, spread operator, an arrow func as a wrapper, and all that jazz.
Desired behavior: Every time I check a checkbox, I want to update items [] to push a new object into it, which represents all items that have ever been checked. Before you say anything about duplicating: I'll add the check to see if an item is already in the array, and just update it if so. And before I add all items to cart, I'll strip all objects with checked = false states.
Can you help me understand what react lifecycle fundamentals I'm missing here; why is this happening? And how can I fix it?
CODE:
Where this is happening:
Simplified version of InputComponent
const InputComponent = ({ type, itemId, handleSearchQuery, onSubmit }) => {
const [items, setItems] = useState([]);
const captureInput = (evt) => {
if (evt.target.type === 'checkbox') {
setItems((prevState) => [...prevState, { [evt.target.value]: evt.target.checked }]);
}
};
const renderCheckbox = () => {
return (
<form>
<input type={type} name={itemId} value={itemId} onChange={setItem} />
<input name={itemId} type='submit' value='Add to Cart' />
</form>
);
};
return (
<div className='input-bar'>
{renderCheckbox()}
</div>
);
};
export default InputComponent;
Where this component is used:
import React from 'react';
import InputComponent from './InputComponent';
import './ResultsRenderer.css';
function ResultsRenderer({ data }) {
const renderListings = () => {
let listings = data ? data.Search : null;
return listings
? listings.map((item) => {
return (
<div className='cart-row'>
<InputComponent type='checkbox' className='cart-checkbox' itemId={item.imdbID} />
<div key={item.imdbID} className={item.imdbID}>
<img src={`${item.Poster}`} alt={item.Title} />
<div>
Title<em>{item.Title}</em>
</div>
<div>{item.Year}</div>
<em>{item.imdbID}</em>
</div>
</div>
);
})
: null;
};
return <>{renderListings()}</>;
}
export default ResultsRenderer;
items state is doing its job perfectly fine, you misunderstood the situation.
you're using items state inside InputComponent and for each listings item there is one InputComponent and each one have their own items, I think you meant to use items state inside ResultsRenderer Component to chase all selected items.
here is the changes you need to do:
const InputComponent = ({ type, itemId, setItems }) => {
const captureInput = (evt) => {
if (evt.target.type === "checkbox") {
setItems((prevState) => [
...prevState,
{ [evt.target.value]: evt.target.checked }
]);
}
};
return (
<div className="input-bar">
<form>
<input
type={type}
name={itemId}
value={itemId}
onChange={captureInput}
/>
<input name={itemId} type="submit" value="Add to Cart" />
</form>
</div>
);
};
export default InputComponent;
function ResultsRenderer() {
const [items, setItems] = useState([]);
useEffect(() => {
console.log(items);
}, [items]);
const renderListings = () => {
let listings = [
{ itemId: 1, title: "Hello" },
{ itemId: 2, title: "World" }
];
return listings
? listings.map((item) => {
return (
<div className="cart-row">
<InputComponent
type="checkbox"
className="cart-checkbox"
itemId={item.itemId}
setItems={setItems}
/>
<div key={item.itemId} className={item.itemId}>
<div>
Title<em>{item.Title}</em>
</div>
</div>
</div>
);
})
: null;
};
return <>{renderListings()}</>;
}
and here is the working demo: https://codesandbox.io/s/boring-cookies-t0g4e?file=/src/InputComponent.jsx

when I click add button multiple times it should show I am here div with delete button with multiple times

I am trying to build a simple ui so that I can learn react.
right now when I click an add button it will show I am here div and delete button
when I click add button multiple times it should show I am here div with delete button with multiple times.
so I research and found this example https://www.skptricks.com/2018/06/append-or-prepend-html-using-reactjs.html
using this example I implemented the appendData method but still its not adding the div multiple times.
in my console I am able to see how many times divs are added console.log("this.displayData---->", this.displayData);
can you tell me how to fix it.
so that in future I will fix it myself
https://stackblitz.com/edit/react-b2d3rb?file=demo.js
onClick = () => {
this.setState({ showResults: true });
this.setState({ showOrHideSearch: true });
this.displayData.push(
<div style={{ display: this.state.showOrHideSearch ? "" : "none" }}>
{" "}
I am here
<input
ref="rbc-toolbar-label"
type="submit"
value="Delete"
onClick={this.onDelete}
/>
</div>
);
console.log("this.displayData---->", this.displayData);
this.setState({ showdata: this.displayData });
};
First thing is you should not use this.setState multiple times, instead you should do them in one line. And instead of pushing data into class variables, you should set that data into your state variable and the same variable you should use in your render function. It will be good if you can share your complete code..
import React, { Component } from 'react';
import Calendar from 'rc-calendar';
import DatePicker from 'rc-calendar/lib/Picker';
import 'rc-calendar/assets/index.css';
import moment from 'moment';
class CalendarPage extends Component {
constructor(props) {
super(props);
console.log("AsyncValidationForm this.props---->", this.props);
this.state = {
displayData: []
};
}
onClick = () => {
let displayData = [...this.state.displayData];
displayData.push( { text: 'I am here' });
this.setState({ displayData: displayData });
};
onDelete = index => {
let displayData = [...this.state.displayData];
if(index > -1){
displayData.splice(index, 1);
}
this.setState({ displayData: displayData });
};
handleChange = name => event => {
const value = event.target.value;
this.setState(
{
[name]: value,
pristine: false
},
() => {
this.props.handleChange(name, value); //setState username, password of VerticalLinearStepper.js
}
);
};
onSubmit(event) {
event.preventDefault();
var newItemValue = this.refs.itemName.value;
if(newItemValue) {
this.props.addItem({newItemValue});
this.refs.form.reset();
}
}
render() {
const { handleSubmit, pristine, reset, submitting } = this.props;
let { displayData} = this.state;
return (
<form onSubmit={handleSubmit}>
<div>
<input type="submit" value="add" onClick={this.onClick} />
{displayData.length > 0 ? displayData.map(function(data, index) {
return (
<div key={index}>
{data.text} - For testing added index on screen {index}
<input
ref="rbc-toolbar-label"
type="submit"
value="Delete"
onClick={() => this.onDelete(index)}
/>
</div>
)}, this) : null}
</div>
</form>
);
}
}
export default CalendarPage;

(ReactJS) List of items doesn't get updated on page, even though database gets updated

I have this single page application that uses firebase and reactjs/nodejs that updates/removes/adds html code (body and description). The functions work fine and the data gets updated accordingly in the database. However, the list doesn't refresh once I do any function (only if I press F5 manually). I want the list to dynamically change as I add/edit/delete any of the content. How can I do this?
Here's my code:
HTML.js:
const updateByPropertyName = (propertyName, value) => () => ({
[propertyName]: value,
});
class HTML extends Component {
constructor(props) {
super(props);
this.state = {
html: []
};
}
componentDidMount() {
db.onceGetHTML().then(snapshot =>
this.setState(() => ({ html: snapshot.val() }))
);
}
render() {
const { html } = this.state;
const { description } = this.state;
const { body } = this.state;
return (
<div>
<h1>Home</h1>
<p>The Home Page is accessible by every signed in user.</p>
<input value={description}
onChange={event => this.setState(updateByPropertyName('description', event.target.value))}
type="text"
placeholder="Description..."
/>
<input value={body}
onChange={event => this.setState(updateByPropertyName('body', event.target.value))}
type="text"
placeholder="Body..."
/>
<button onClick={() => addHTML(description, body)}>Add Content</button>
{!!html && <HTMLList html={html} />}
</div>
);
}
}
These are all in one file, I just split them to make it easier to read (HTML.js):
function addHTML(description, body, callback) {
addAnHTML(description, body);
}
And here's the 2nd class in the same file that is responsible for displaying the list of items:
class HTMLList extends Component {
constructor(props) {
super(props);
this.state = {
BODY: '',
desc: '',
html: ''
};
}
render() {
const { html } = this.props;
const { desc } = this.state;
const { BODY } = this.state;
return (
<div>
<h2>List of HTML available:</h2>
{Object.keys(html).map((key, index) =>
<div>
{index + 1}.
{html[key].description}
<img src="http://www.stilltimecollection.co.uk/images/english/b_delete.gif" onClick={() => deleteHTML(key)} />
<Popup trigger={<img src="https://www.faktorzehn.org/de/wp-content/uploads/sites/2/2015/03/f10-org-new_3_6_0-edit.gif" />
} position="right center">
<div>
<input value={desc}
onChange={event => this.setState(updateByPropertyName('desc', event.target.value))}
type="text"
placeholder="Descripton.."
/>
<input value={BODY}
onChange={event => this.setState(updateByPropertyName('BODY', event.target.value))}
type="text"
placeholder="Body..."
/>
<button onClick={() => updateHTML(key, desc, BODY)}>Update Content</button>
</div>
</Popup>
<br></br>
</div>
)}
</div>
);
}
}
The function addAnHTML is in a different file where the database is synchronized:
export const addAnHTML = (description, body) => {
var html =
{
description: description,
body: body,
created_at: format.asString(),
updated_at: ""
}
db.ref('Content').push(html);
alert("Content Added");
}
My page looks like this:
https://preview.ibb.co/fTwiaT/Untitled.png
My database looks like this (added in db but not dynamically):
https://image.ibb.co/nNEapo/database.png
EDIT: so here's the edit on the functions I use:
export const onceGetHTML = () =>
db.ref('Content').once('value');
export const addAnHTML = (description, body) => {
console.log(description);
console.log(body);
var html =
{
description: description,
body: body,
created_at: format.asString(),
updated_at: ""
}
db.ref('Content').push(html);
}
and the edited add function in my class looks like this:
addContent(description, body) {
this.setState({
html: [
...this.state.html,
{
description: this.state.description,
body: this.state.body
}
]
});
addAnHTML(this.state.description,this.state.body);
}
Snapshot.val() contains all the child values of my "Content" parent:
https://preview.ibb.co/jetOc8/edit.png
I will showcase how to correctly map your data to the DOM for the "add" functionality.
Here is the modified HTML class
export class HTML extends Component {
constructor(props) {
super(props);
this.state = {
html: [],
// if you keep your user inputted data in the DOM state, it's good to initialize them first, otherwise your component will suddenly change from an uncontrolled component to a controlled component.
description: "",
body: ""
};
}
componentDidMount() {
// EDIT: code here for initializing `html`
db.onceGetHTML().then(snapshot =>
// EDIT: it's simpler to just call setState with the new state object you want
this.setState({ html: snapshot.val()})
);
}
// use class methods to update your DOM state, because inside these methods
// also don't forget to call your API here
addContent(description, body) {
// EDIT: make sure to make your call to the db for adding an entry
this.setState({
// please keep in mind that you should NEVER mutate your state, so in here I'm using the spread operator to create a new array instance
// this is appending a new entry to the old "html"
html: [
...this.state.html,
{
description: this.state.description,
body: this.state.body
}
]
});
}
// use this to update the state for each input field
updateByPropertyName(property, e) {
this.setState({
[property]: e.target.value
});
}
render() {
const { html } = this.state;
const { description } = this.state;
const { body } = this.state;
return (
<div>
<h1>Home</h1>
<p>The Home Page is accessible by every signed in user.</p>
<input
value={description}
onChange={this.updateByPropertyName.bind(this, "description")}
type="text"
placeholder="Description..."
/>
<input
value={body}
onChange={this.updateByPropertyName.bind(this, "body")}
type="text"
placeholder="Body..."
/>
// this onClick event will call "this.setState(...)" which will trigger the re-render (or the "refresh" you are looking for)
<button onClick={this.addContent.bind(this)}>Add Content</button>
{!!html && <HTMLList html={html} />}
</div>
);
}
}
This will be enough for your successful re-render.
On a side note, it's also useful to index your elements in the list. It's really useful for React to render efficiently. For that, you can modify your HTMList class like so:
//...
<h2>List of HTML available:</h2>
{Object.keys(html).map((key, index) => (
// using the index parameter supported in the .map callback it's okay
// because it makes your DOM list nodes predictable and easy for React to re-render
<div key={index}>
{index + 1}.
{html[key].description}
<img
//...
For the update and delete you can follow the same pattern, only change the state (in which you hold the html object) accordingly.
You can use these readings for the stuff I just explained above
controlled vs uncontrolled components
handling lists components in React

Error dynamically updating an array list in ReactJs

I am learning reactJS and so I am trying my hands on an example. This example has a form textfield that can add an item to an existing array on click of a button. I am having errors here as when I enter a text and click on the button, the array list is not updated except I try to make changes to the text entered in the textfield. This is what I am doing:
import React, { Component } from 'react';
class App extends Component {
constructor(props){
super(props);
this.state = {
currentName : '',
arrays : ['john', 'james', 'timothy']
}
}
render() {
const showNames = this.state.arrays.map((thisName) => {
const values = <li>{thisName}</li>;
return values;
});
const getText = (e) => {
let value = e.target.value;
this.setState({
currentName : value
})
}
const addToUsers = () => {
this.state.arrays.push(this.state.currentName)
}
return (
<div>
<p>Add new name to List</p><br/>
<form>
<input type="text" onChange={getText}/>
<button type="button" onClick={addToUsers}>Add User</button>
</form>
<ul>
{showNames}
</ul>
</div>
);
}
}
export default App;
There are a host of things wrong with this, but your issue is likely that you need to use setState to modify state.
import React, { Component } from 'react';
class App extends Component {
constructor(){
super();
this.state = {
names: ['john', 'james', 'timothy']
}
}
addToUsers = () => {
this.setState(
prevState => ({
names: [...prevState.names, this.input.value]
})
)
}
render() {
const names = this.state.names.map(
(name, index) => <li key={index}>{name}</li>
)
return (
<div>
<p>Add new name to List</p><br/>
<form>
<input type="text" ref={e => this.input = e} />
<button type="button" onClick={this.addToUsers}>Add User</button>
</form>
<ul>
{names}
</ul>
</div>
)
}
}
export default App;
This quick edit changes a few things:
Uses setState for the addToUsers method
Eliminate onChange tracking and pull the name directly from the input when the button is clicked
Move the addToUsers method out to the component class rather than defining it on render
Rename this.state.arrays to this.state.names
Simplify conversion of this.state.names into list items
Set key on array elements (name list items)
Use prevState in setState to avoid race conditions
You need to make sure you update state using the setState method.
When you update arrays you are reaching into the state object and manipulating the data directly instead of using the method.
Instead try something like:
const addToUsers = () => {
const newArray = this.state.arrays.concat([this.state.currentName]);
this.setState({
arrays: newArray
});
}
You probably must add
onChange={getText}.bind(this)
to your functions.
Also change this
const addToUsers = () => {
this.state.arrays.push(this.state.currentName)
}
to this
const addToUsers = () => {
this.setState({here put your variable})
}

React - Update Value Of Input After Submit

I'm trying to make a feature where a user can edit a submitted value. So to be completely clear:
You would enter some text
Click submit and that value will be pushed into an array
You will be able to see your value on the dom
If you made an error, you can click on that input and change the value, also updating the state of that value in the already pushed array.
On a button click, you will update the state and have a newly edited value.
I'm stuck on the part of changing the state of the value of the pushed items in the array.
For example:
If I were to click on the field of 'Bob', edit it and click submit, the value of whatever I changed it to would also change the state of what was originally in my array to the new value.
This is what I have so far:
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
notes: ['hello', 'bob'],
val: ''
}
}
submit = () => {
const { notes, val } = this.state
notes.push(val)
this.setState({notes})
}
handleEdit = e => {
console.log(e)
}
render() {
return (
<div>
<input
type="text"
onChange={e => this.setState({val: e.target.value})}
/>
<button onClick={this.submit}>Submit</button>
{this.state.notes.map(item => {
return (
<form onSubmit={e => e.preventDefault()}>
<input
type="text"
defaultValue={item}
onChange={e => this.setState({val: e.target.value})}
/>
<button onClick={() => this.handleEdit(item)}>Submit
Change</button>
</form>
)
})}
</div>
)
}
}
Try this kind of thing :
handleEdit = (item) => {
const notes = this.state.notes.slice();
const index = notes.indexOf(item);
notes[index] = this.state.val;
this.setState({
notes
})
}

Categories

Resources