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.
Related
I'm struggling while creating an element that is passed by the .map function. Basically, I want my webpage to create a div element with some date in it when a button is clicked for that I'm using a .map function but it isn't working out.
const handleSubmit = (e) => {
e.preventDefault();
const data = {title:`${title}`, desc:`${desc}`, date:`${date}`};
data.map(userinfo =>{
return(<div>
<h1>{userinfo.title}</h1>
</div>)
})
console.log(data);
}
In reactJS, if we want to display our data in HTML webpage we usually do that in the render funciton.
We can use userInfo variable in the state object.
The userInfo data is hardcoded for demonstration purposes but you can also populate the userInfo variable either using API or in any other way you like.
Moreover, showUserInfo is another variable (initially false) that would render the data once it is set to true
this.state = {
userInfo: [
{
title: 'one',
desc: '',
date: new Date()
},
{
title: 'two',
desc: '',
date: new Date()
}
],
showUserInfo: false
}
On a click event we can set showUserInfo to true using setState function.
more on setState function via this link ->
https://medium.com/#baphemot/understanding-reactjs-setstate-a4640451865b
handleSubmit = async (event) => {
event.preventDefault();
this.setState(
{
...this.state,
showUserInfo: true
}
)
}
In the render function, if showUserInfo is false then userInfo.map is never going to render unless showUserInfo is set to true which we do using a click listener that is associated with our function handleSubmit.
render(){
return (
<div>
<button onClick={this.handleSubmit}>Click Me</button>
{ this.state.showUserInfo &&
this.state.userInfo.map(item =>(
<div>
<p> {item.date.toString()} </p>
</div>
) ) }
</div>
);
}
Overall the result looks a something like this.
export default class App extends React.Component {
constructor() {
super();
this.state = {
showUserInfo: false,
userInfo: [
{
title: 'one',
desc: '',
date: new Date()
},
{
title: 'two',
desc: '',
date: new Date()
}
],
}
}
handleSubmit = async (event) => {
event.preventDefault();
this.setState(
{
...this.state,
showUserInfo: true
}
)
}
render(){
return (
<div>
<button onClick={this.handleSubmit}>Click Me</button>
{ this.state.showUserInfo &&
this.state.userInfo.map(item =>(
<div>
<p> {item.date.toString()} </p>
</div>
) ) }
</div>
);
}
}
I am running into a problem with the Material UI rating component.
I want it to return a number when I change it, but it returns a string. The initial value I provide (this.props.data.feelings_rating) is a number, but whenever I change it in my app it becomes a string. Here is the relevant code:
Rating component:
<Rating
value={this.props.data.feelings_rating}
name="feelings_rating"
onChange={handleChange}
size="large"
getLabelText={(value) => customIcons[value].label}
IconContainerComponent={IconContainer}
/>
handleChange:
handleChange = (e) => {
var diary = this.state.diary
diary[e.name] = e.value
this.setState({diary: diary})
}
other stuff to do with the icons:
const customIcons = {
1: {
icon: <SentimentVeryDissatisfiedIcon />,
label: 'Very Dissatisfied',
},
2: {
icon: <SentimentDissatisfiedIcon />,
label: 'Dissatisfied',
},
3: {
icon: <SentimentSatisfiedIcon />,
label: 'Neutral',
},
4: {
icon: <SentimentSatisfiedAltIcon />,
label: 'Satisfied',
},
5: {
icon: <SentimentVerySatisfiedIcon />,
label: 'Very Satisfied',
},
};
function IconContainer(props) {
const { value, ...other } = props;
return <span {...other}>{customIcons[value].icon}</span>;
}
IconContainer.propTypes = {
value: PropTypes.number.isRequired,
};
I cannot find anyone else on the internet with this problem, so can anyone spot what I am doing wrong? Any answers would be appreciated.
The onChange of the Rating component gives you 2 arguments one is the event object and the other is actual value. You can retrive the selected value from the second argument newValue
handleChange = (e, newValue) => {
// create a copy of the state
const clonedDiary = {...this.state.diary}
// now mutate the clonedDiary directly
clonedDiary[e.name] = newValue;
this.setState({diary: clonedDiary })
}
Refer
Rating API
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));
I'm currently writing my first react application and my ESLINT is telling me that I shouldn't be using .bind() on JSX props. I understand that this is because bind is creating new functions and therefore negatively affecting performance. However i am not sure how to refactor this to eliminate this error.
How can i pass the element I have clicked to the function without using a bind?
ForecastPage.jsx:
import React from 'react'
import api from '../shared/api'
import ForecastBox from './ForecastBox'
import DropdownSelector from './DropdownSelector'
const regions = [
{
name: 'Santa Cruz',
id: '2958',
spots:
[
{ name: 'Steamer Lane', id: '4188' },
{ name: 'Four Mile', id: '5023' },
{ name: 'Waddell Creek', id: '5021' },
{ name: 'Mitchell\'s Cove', id: '5028' },
{ name: '26th Ave', id: '5030' },
],
},
{
name: 'North Orange Country',
id: '2143',
spots:
[
{ name: 'Newport', id: '1241' },
{ name: 'HB', id: '3421' },
],
},
]
class ForecastPage extends React.Component {
constructor(props) {
super(props)
this.state = {
selectedRegion: null,
selectedSpot: null,
forecast: null,
}
this.regionSpotList = regions
this.updateSpot = this.updateSpot.bind(this)
this.updateRegion = this.updateRegion.bind(this)
}
updateRegion(region) {
this.setState({
selectedRegion: region,
forecast: null,
})
api.fetchSpot(region.id)
.then((forecast) => {
this.setState({
forecast,
})
})
}
updateSpot(spot) {
this.setState({
selectedSpot: spot,
forecast: null,
})
api.fetchSpot(spot.id)
.then((forecast) => {
this.setState({
forecast,
})
})
}
render() {
return (
<div>
<div className="container-fluid row region-spot-select">
<DropdownSelector
options={this.regionSpotList}
onSelect={this.updateRegion}
title={this.state.selectedRegion == null ? 'Select Your Region' : this.state.selectedRegion.name}
keyName={'region-selector'}
id={'region-selector-dropdown'}
/>
{this.state.selectedRegion != null &&
<DropdownSelector
options={this.state.selectedRegion.spots}
onSelect={this.updateSpot}
title={this.state.selectedSpot == null ||
!this.state.selectedRegion.spots.includes(this.state.selectedSpot) ?
'Select A Spot' :
this.state.selectedSpot.name}
keyName={'spot-selector'}
id={'spot-selector-dropdown'}
/>
}
</div>
<div>
{!this.state.forecast ?
<div>
Select A Region
</div>
: <ForecastBox forecast={this.state.forecast} /> }
</div>
</div>
)
}
}
export default ForecastPage
DropdownSelector.jsx
// #flow
import React from 'react'
import PropTypes from 'prop-types'
import { DropdownButton, MenuItem } from 'react-bootstrap'
type Props = {
options: Object,
onSelect: Function,
title: string,
keyName: string,
id: string,
}
const DropdownSelector = ({ title, options, keyName, id, onSelect }: Props) =>
<div className="content">
<div className="btn-group">
<DropdownButton
bsStyle={'primary'}
title={title}
key={keyName}
id={id}
>
{options.map(element =>
<MenuItem
key={element.name}
eventKey={element.name}
// eslint-disable-next-line
onClick={onSelect.bind(null, element)}
>
{element.name}
</MenuItem>,
)
}
</DropdownButton>
</div>
</div>
DropdownSelector.defaultProps = {
id: null,
}
DropdownSelector.propTypes = {
options: PropTypes.instanceOf(Object).isRequired,
title: PropTypes.string.isRequired,
onSelect: PropTypes.func.isRequired,
keyName: PropTypes.string.isRequired,
id: PropTypes.string,
}
export default DropdownSelector
Try Alex's answer, but just onSelect, without 'this'.
You could also use an arrow function which would accomplish the same thing, so something like
onClick={(event) => this.props.onSelect(null, element)}
However, it has the same potential negative performance problem you mentioned. The React docs are very good in this area and enumerate your options and their pro's and cons: https://facebook.github.io/react/docs/handling-events.html
Updated to this.props.onSelect, forgot that you were passing that in as a prop as opposed to defining it on the component itself. And if you're not using the event object, perhaps just use
onClick={() => this.props.onSelect(null, element)}
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")
);