How to append data in React JS - javascript

I need to append this data response example in my React app.
DATA Response
[
{
"trackInfo": {
"id": 1,
"title": "Guns & Dogs",
"artist": "Portugal, The Man",
"album": "The Satanic Satanist"
},
"trackUrl": "https://s3-us-west-2.amazonaws.com/teddarcuri.monarch/Portugal.+The+Man+-+Guns+%26+Dogs+-+The+Satanic+Satanist.mp3",
"albumArt": "http://ecx.images-amazon.com/images/I/61X7CiBpZ6L.jpg"
}
]
React JS
class App extends React.Component {
constructor(props){
super(props)
this.state = { //initial empty details
details : {}
}
}
componentDidMount(){
//place the ajax call where ever you need
$.ajax() //call ajax
.done((data) => {
this.setState({ //this setState will re render the UI with the new state
details: { //change the key value pairs as needed
id: data.id,
trackInfo: {
title: data.title,
artist: data.artist,
album: data.album,
},
trackUrl: data.trackUrl,
albumArt: data.albumArt,
}
})
})
}
render() {
if(!this.state.details.id) return false //renders nothing when no details available
return (
<div id="app">
<MusicPlayer
id={this.state.details.id}
visualizerType="RIPPLES"
theme={darkTheme}
trackInfo={this.state.details.trackInfo}
trackUrl={this.state.details.trackUrl}
albumArt={this.state.details.albumArt}
utilities={true}>
</MusicPlayer>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById("app")
);
Full code example here
Working example with preloaded data here
So my question is, how can I append the new data in React using Ajax?
A code example will be really appreciate, thanks.

I think you want to show a list of MusicPlayer, so I changed your code:
[you need to read more about state in react]
class App extends React.Component {
constructor(props){
super(props)
this.state = { //initial empty details
details : [] // use array
}
}
componentDidMount(){
//place the ajax call where ever you need
$.ajax() //call ajax
.done((data) => {
let array = this.state.details;
array = [...array, {
id: data.id,
trackInfo: {
title: data.title,
artist: data.artist,
album: data.album,
},
trackUrl: data.trackUrl,
albumArt: data.albumArt,
}];
this.setState({
details: array
})
})
}
render() {
if(!this.state.details.id) return false //renders nothing when no details available
return (
<div id="app">
{
this.state.details.map((detail) => {
return <MusicPlayer
id={detail.id}
visualizerType="RIPPLES"
theme={darkTheme}
trackInfo={detail.trackInfo}
trackUrl={detail.trackUrl}
albumArt={detail.albumArt}
utilities={true}>
</MusicPlayer>
});
}
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById("app")
);

Related

React: Can't add a new task to my tasks's array

I'm new to React and I'm trying create a To Do List project.
I'm trying to add a new task to my tasks's array via input, but when I press Enter nothing is added to screen. Can someone help?
App.js
import React, { Component } from "react";
import Tasks from "./Components/tasks";
class App extends Component {
constructor(props) {
super(props);
this.state = {
newTask: '',
tasks: [
{ id: 1, text: "study" },
{ id: 2, text: "read" },
{ id: 3, text: "gym" },
]
};
}
handleSubmit(e) {
e.preventDefault();
const tasks = [...this.state.tasks];
tasks.push({id: 4, text: this.state.newTask});
this.setState({ tasks: tasks });
}
handleChange= (e) => {
this.setState({newTask: e.target.value});
}
render() {
return (
<div className="App">
<h1>To Do List</h1>
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="Enter task" value={this.state.newTask} onChange={this.handleChange}/>
</form>
<Tasks tasks={this.state.tasks} />
</div>
);
}
}
export default App;
Adicionaly I'm getting this error on the console:
error
you need to bind your function to the class
simple solution is to use arrow function syntax
handleSubmit = (e) => {
instead of
handleSubmit(e) {
there are other ways to do it as well..
you can read this article to understand more https://www.freecodecamp.org/news/this-is-why-we-need-to-bind-event-handlers-in-class-components-in-react-f7ea1a6f93eb/

React component loads data twice on moving back-and-forth between tabs

For some reason my React component seems to remember its old state when going to another tab and back again, instead of reloading completely.
Basically when I click on the "Create" tab in my navbar and back to the "Board" tab data is populated twice instead of once, see image below. When going back the Board component this.state has two of each taskIds, as if it the component state still had the data from the initial page load when loading again. I have a React component looking like this:
const columnOrder = ['todo', 'in-progress', 'in-review', 'done']
const EMPTY_COLUMNS = {
'todo': {
id: 'todo',
title: 'TODO',
taskIds: []
},
'in-progress': {
id: 'in-progress',
title: 'In Progress',
taskIds: [],
},
'in-review': {
id: 'in-review',
title: 'In Review',
taskIds: []
},
'done': {
id: 'done',
title: 'Done',
taskIds: []
}
};
export class Board extends Component {
constructor(props) {
super(props);
this.onLoadEpic = this.onLoadEpic.bind(this);
this.state = {
columnOrder: columnOrder,
columns: {
'in-progress': {
id: 'in-progress',
title: 'In Progress',
taskIds: [],
},
// ...more columns similar to above
},
};
// Load state data on mount
componentDidMount() {
loadEpic(arg1, arg2);
}
// Async function loading items from DB and formatting into useful columns
async loadEpic(arg1, arg2) {
axios.get(...)
.then((response) => {
let data = response.data;
let newTasks = {};
let newColumns = EMPTY_COLUMNS;
data.taskItems.forEach(function(item) {
let id = item.id.toString();
newColumns[item.status]["taskIds"].push(id);
newTasks[id] = {
...item,
id: id
}
});
this.setState({
tasks: newTasks,
columns: newColumns
});
})
}
render() {
// Prints ["7"] on initial load and ["7", "7"] after going back and forth
console.log(this.state.columns["in-progress"].taskIds);
return (
// Simplified, but this is the main idea
<Container>
<DragDropContext onDragEnd={this.onDragEnd}>
{
this.state.columnOrder.map((columnId) => {
const column = this.state.columns[columnId]
const tasks = column.taskIds.map(taskId => this.state.tasks[taskId]
return (
<Column key={column.id} column={column} tasks={tasks}/>
)
}
}
</DragDropContext>
</Container>
)
}
}
and an App.js with Routing looking like this:
export default class App extends Component {
static displayName = App.name;
render () {
return (
<Layout>
<Route exact path='/' component={Board} />
<Route exact path='/create' component={Create} />
</Layout>
);
}
}
Okay, so I figured it out: it's the EMPTY_COLUMNS constant that is bugging out. When the component is re-rendered, the same EMPTY_COLUMNS object is referenced - so the constant is being appended to. Instead, I should make a copy of the empty columns:
// Before - same object is being appended to, doesn't work
let newColumns = EMPTY_COLUMNS;
// After - create a deep copy of the constant, does work
let newColumns = JSON.parse(JSON.stringify(EMPTY_COLUMNS));

How do I pass props in from a separate component?

I am trying to get my head around props and how they work exactly. Here is my layout so far.
I have created a page called "TodoData.js" which has all of my Todos
const todoss = [
{
id: 1,
text: "First Todo"
},
{
id: 2,
text: "Second Todo"
},
{
id: 3,
text: "Third Todo"
},
{
id: 4,
text: "Fourth Todo"
}
]
export default todoss;
I then have my main page "Todolist.js", I have imported the data with "import TodoData from './TodoData'" at the top but I can't figure out exactly how to take that data and map it out onto the page, how would i do this?
You can use map() function to iterate an array.
import TodoData from './TodoData'
render() {
return (
<div>
{TodoData.map(function(data, idx){
return (<li key={idx}>{data.id}:{data.text}</li>)
})}
</div>
);
}
This is the output:
1:First Todo
2:Second Todo
3:Third Todo
4:Fourth Todo
You can use any styling you need.
Saving data internally as state is the "React" way of handling data.
In a real world application this data is going to come from an external source and if the developer doesn't know how to save data internally he will have no idea what to do.
components-and-props
state
Don't import the data, save it in the state of your Todos component and pass it as props to Todolist.
// this will act as a presentation of our data
const TodosList = ({ todos }) => (
<ul>
{todos.map(({ id, text }) => (
<li key={id}>{text}</li>
))}
</ul>
);
// This will act as a container for our data
class Todos extends React.Component {
state = {
todos: [
{
id: 1,
text: "First Todo"
},
{
id: 2,
text: "Second Todo"
},
{
id: 3,
text: "Third Todo"
},
{
id: 4,
text: "Fourth Todo"
}
]
};
render() {
return <TodosList todos={this.state.todos} />;
}
}

List item strike-through react

I have a todolist in React, I can delete todo-s but I want to apply strike-through for completed todos. After that it would be great to list them as completed. How is it possible? What should I change in my code? I tried to use objects in the array, but that lead to diff, erros.
import React, { Component } from 'react';
class ToDoList extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
items: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleRemove = this.handleRemove.bind(this);
}
handleChange(event) {
this.setState({items: event.target.value})
console.log(event.target.value);
}
handleSubmit(event) {
this.setState({
list: [...this.state.list, this.state.items],
items: ''
})
event.preventDefault();
}
handleRemove(index) {
const filteredArray = this.state.list.filter((_, i) => i !== index); // used underscore as a convention to address nothing is going there
this.setState({
list: filteredArray
});
}
render() {
return (
<div className='header main'>
<form onSubmit={this.handleSubmit} >
<label>
<input className='new-todo'
placeholder='What needs to be done?'
type="text"
value={this.state.items}
onChange={this.handleChange} />
</label>
</form>
<ul className='todo-list'>
{this.state.list.map((item, index) => (
<li className='list-view' key={index+1}>{item}<button className='list-view-button' onClick={this.handleRemove.bind(this, index) }>X</button></li>
))}
</ul>
<div className='footer'>
Remaining: {this.state.list.length}
</div>
</div>
);
}
}
export default ToDoList;
Well currently you only have an array of strings that represents the todos.
I would do this for your items state:
items: [
{
desc: "todo content",
status: "new"
},
{
desc: "todo content",
status: "completed"
},
{
desc: "todo content",
status: "archived"
}
];
now when you loop through the todos you can check for the status for different design display.
Or you can filter the todos, for specific status,
ie:
this.state.items.filter(item => item.status==="new")
this will give you only the "new" todos.

How to POST new data via Ajax on React JS

Take in consideration this example
I'm really new at React, so this is how I create my new app
class App extends React.Component {
constructor(props){
super(props)
this.state = { //initial empty details
details : {}
}
}
componentDidMount(){
//place the ajax call where ever you need
$.ajax() //call ajax
.done((data) => {
this.setState({ //this setState will re render the UI with the new state
details: { //change the key value pairs as needed
id: data.id,
trackInfo: {
title: data.title,
artist: data.artist,
album: data.album,
},
trackUrl: data.trackUrl,
albumArt: data.albumArt,
}
})
})
}
render() {
if(!this.state.details.id) return false //renders nothing when no details available
return (
<div id="app">
<MusicPlayer
id={this.state.details.id}
visualizerType="RIPPLES"
theme={darkTheme}
trackInfo={this.state.details.trackInfo}
trackUrl={this.state.details.trackUrl}
albumArt={this.state.details.albumArt}
utilities={true}>
</MusicPlayer>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById("app")
);
The plan is to create a button where on click it display a new MusicPlayer element. Eg.
<MusicPlayer
id="3"
visualizerType="RIPPLES"
theme={lightTheme}
trackInfo={{
title: "Guns & Dogs",
artist: "Portugal, The Man",
album: "The Satanic Satanist"
}}
trackUrl="https://s3-us-west-2.amazonaws.com/teddarcuri.monarch/Portugal.+The+Man+-+Guns+%26+Dogs+-+The+Satanic+Satanist.mp3"
albumArt="http://ecx.images-amazon.com/images/I/61X7CiBpZ6L.jpg"
utilities={true}>
</MusicPlayer>
How can I proper POST new JSON data via Ajax to the render?
Assuming your original ES6/JSX looks something like this:
class App extends React.Component {
constructor() {
super();
this.state = {
details: {},
};
}
componentDidMount() {
$.ajax() // call ajax
.done(data => {
this.setState({ // this setState will re render the UI with the new state
details: { // change the key value pairs as needed
id: data.id,
trackInfo: {
title: data.title,
artist: data.artist,
album: data.album,
},
trackUrl: data.trackUrl,
albumArt: data.albumArt,
},
});
});
}
render() {
return (
<div>
<MusicPlayer
{...{
id: this.state.details.id,
visualizerType: 'RIPPLES',
theme: darkTheme,
trackInfo: this.state.details.trackInfo,
trackUrl: this.state.details.trackUrl,
albumArt: this.state.details.albumArt,
utilities: true,
}}
/>
</div>
);
}
}
and you want to make that $.ajax call again for some new player data on a button click. You need to create a method for the the click handler that can update the component's state. This would look like:
class App extends React.Component {
constructor() {
super();
this.state = {
details: {},
};
// this is important so that the getNewData method will have the correct "this" context on click
this.getNewData = this.getNewData.bind(this);
}
componentDidMount() {
$.ajax() // call ajax
.done(data => {
this.setState({ // this setState will re render the UI with the new state
details: { // change the key value pairs as needed
id: data.id,
trackInfo: {
title: data.title,
artist: data.artist,
album: data.album,
},
trackUrl: data.trackUrl,
albumArt: data.albumArt,
},
});
});
}
getNewData(e) {
e.preventDefault();
// any logic to set up url or params for the ajax call can be done here
$.ajax() // call ajax
.done(data => {
this.setState({ // this setState will re render the UI with the new state
details: { // change the key value pairs as needed
id: data.id,
trackInfo: {
title: data.title,
artist: data.artist,
album: data.album,
},
trackUrl: data.trackUrl,
albumArt: data.albumArt,
},
});
});
}
render() {
return (
<div>
<MusicPlayer
{...{
id: this.state.details.id,
visualizerType: 'RIPPLES',
theme: darkTheme,
trackInfo: this.state.details.trackInfo,
trackUrl: this.state.details.trackUrl,
albumArt: this.state.details.albumArt,
utilities: true,
}}
/>
<button onClick={this.getNewData}>Click me!</button>
</div>
);
}
}
This is a simple way to achieve what you want, but the data is localized to this component. If you have a complex app you may want to do this with Redux and async actions or middleware, but that requires a more complex application setup.

Categories