I am trying to pass date from a input flild to a state and pass it into API. I am not able to do this in componentDidMount() but I am able to pass it in componentDidUpdate()
The problem is the that it becomes a endless loop.
export default class Med extends React.Component {
constructor(props) {
super(props);
this.state = {
dateState: [],
};
this.dateHandler = this.dateHandler.bind(this);
}
dateHandler = (e) => {
var dateselected = e.target.value;
var date = moment(e.target.value).format("YYYY-MM-DD");
console.log(date);
this.setState({ dateState: date });
};
componentDidMount() {
console.log("====================================");
console.log(this.state.dateState); //not able to console.log
console.log("====================================");
const config = {
headers: {
Authorization: `token ` + localStorage.getItem("token"),
},
};
axios.get("customer/medGET/?date=" + this.state.dateState, config).then((res) => {
this.setState({ items: res.data });
});
}
render() {
return (
<table>
<tr>
<input type="date" onChange={this.dateHandler} />
</tr>
</table>
);
}
}
This simplified version of your code shows that the state is available just fine in componentDidMount.
class Med extends React.Component {
constructor(props) {
super(props);
this.state = {
dateState: ["1"],
};
}
componentDidMount() {
console.log("====================================");
console.log(this.state.dateState);
console.log("====================================");
}
render() {
return (
<table>
<tr>
<input type="date" />
</tr>
</table>
);
}
}
ReactDOM.render(<Med />, document.getElementById("x"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="x"></div>
Related
I've managed to work out how to give the note key in localStorage a value from the addNote page but I can't seem to work out how to make it so that it creates a new value under the note key and not just re-assigning the note value. I'm believe having either a for loop which will loop to the next array index or if statement would fix this issue and allow for the adding of items however I'm just unsure where to place it. Any help in working this issue out would be greatly appreciated :)
Download React project files here: https://drive.google.com/drive/folders/1_P85WUmyY9QcZu14-Ib8IxAU3e4UtZCp
Below is the code I'm using to get a input value and add it to the note key in a file called storage.js (located in src/services/storage.js)
class AddNote extends Component {
constructor(props) {
super(props)
this.state = {
//title: [],
//content: []
items: []
}
let note = getLocalItem(keys.note);
if(!note) {
note = [];
}
this.addNote = this.addNote.bind(this);
}
addNote(event) {
console.log("Working")
if( this.theTitle.value !== "" ) {
var newItem = {
title: this.theTitle.value,
content: this.theContent.value
};
}
this.setState((prevState) => {
return {
items: prevState.items.concat(newItem)
};
});
const form = {
title: this.state.title,
content: this.state.content
}
setLocalItem('note', this.theTitle.value + " - " + this.theContent.value);
You just wan't to add the next content field 'note' to the existing value in the localStorage... the following code reads the localStorage value on componentDidMount; and then on each click of the button, updates (instead of replace) the localStorage array...
relevant component:
class App extends Component {
constructor(props) {
super(props);
this.state = {
inputs: [],
lastInput: ''
};
this.click = this.click.bind(this);
}
componentDidMount() {
this.getExistingArray();
}
getExistingArray() {
if (localStorage.getItem('inputs')) {
var storedInputs = localStorage.getItem('inputs');
this.setState({ inputs: storedInputs }, function () { console.log("from localStorage We got:", this.state.inputs); });
}
}
click() {
var newInput = [...this.state.inputs, this.state.lastInput];
localStorage.setItem('inputs', newInput);
this.getExistingArray();
}
recordInput(e) {
this.setState({ lastInput: e.target.value });
}
render() {
return (
<div>
<input type='text' onChange={this.recordInput.bind(this)} />
<button onClick={this.click}>Click to update the Array</button>
<br /> Array: {this.state.inputs}
</div>
);
}
}
complete working stackblitz
UPDATE: following function updated in light of questioner's comment
click() {
var newInput = [...this.state.inputs, this.state.lastInput];
localStorage.setItem('inputs', newInput);
this.getExistingArray();
var newInputTag = 'inputs' + this.state.inputs.length;
localStorage.setItem(newInputTag, this.state.lastInput);
}
UPDATE2:
All local storage objects retrieved and printed on the page
class App extends Component {
constructor(props) {
super(props);
this.state = {
inputs: [],
lastInput: '',
localStoragePairs: []
};
this.click = this.click.bind(this);
}
componentDidMount() {
this.getExistingArray();
}
getExistingArray() {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
var value = localStorage.getItem(key);
var updatedLocalStoragePairs = this.state.localStoragePairs;
updatedLocalStoragePairs.push({ 'keyName': key, 'valueName': value });
this.setState({ localStoragePairs: updatedLocalStoragePairs });
}
console.log("complete localStoragePairs:", this.state.localStoragePairs);
if (localStorage.getItem('inputs')) {
var storedInputs = localStorage.getItem('inputs');
this.setState({ inputs: storedInputs }, function () { console.log("from localStorage We got:", this.state.inputs); });
}
}
click() {
var newInput = [...this.state.inputs, this.state.lastInput];
localStorage.setItem('inputs', newInput);
this.getExistingArray();
var newInputTag = 'inputs' + this.state.inputs.length;
localStorage.setItem(newInputTag, this.state.lastInput);
}
recordInput(e) {
this.setState({ lastInput: e.target.value });
}
render() {
var LocalSotrageContent = this.state.localStoragePairs.map((value, index) => {
return <tr key={index}> <td>{value.keyName}</td> <td>{value.valueName}</td> </tr>
});
return (
<div>
<input type='text' onChange={this.recordInput.bind(this)} />
<button onClick={this.click}>Click to update the Array</button>
<table>
<thead>
<tr>
<th>All Local Storage objects by Name</th>
<th>All Local Storage objects by Value</th>
</tr>
</thead>
<tbody>
{LocalSotrageContent}
</tbody>
</table>
<br />
</div>
);
}
}
How to get updated data of react by calling the new data that will be received from another page by ajax?
How to replace new data to "Result" div.
class App extends React.Component {
constructor(props){
super(props);
this.state = {
data: [],
}
$.ajax({
url:"/test.bc",
type:"get",
success:(result)=>{
this.setState({data: eval(result)});
}
})
$(document).on('update_result',(event,startairline,classname,stops)=>{
$.ajax({
url:"/new.bc",
type:"post",
data:{
startairline:startairline,
stops:stops,
classname:classname,
},
success:(result)=>{
console.log(result)
this.setState({hasBeenChanged: true,data:eval(result)})
},
})
});
}
renderFlight(){
return this.state.data.map((item)=>{
return(<input type="hidden" value={item.total} name="total" /> )
} )}
render(){
return(<div>{this.renderFlight()}</div> )
}
}
ReactDOM.render(<App/>, document.getElementById('Result'));
I prepare you an example, using componentDidMount and fetch:
Here working
let { Table } = ReactBootstrap;
class Example extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
products: []
}
}
componentDidMount() {
console.log('componentDidMount..')
fetch('https://api.github.com/users/xiaotian/repos')
.then(response => response.json())
.then(output => {
let products = []
for (let i = 0; i < output.length; i++) {
products.push({selected:false,name:output[i].name})
}
this.setState({products},() => console.log(this.state))
})
}
render() {
return(<Table striped bordered condensed hover>
<thead>
<tr>
<th>Selected</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{this.state.products.map((item, i) => {
return (
<tr><td><input type="checkbox" checked={item.selected}/></td><td>{item.name}</td></tr>
)
})}
</tbody>
</Table>)
}
}
ReactDOM.render(
<Example />,
document.getElementById('app')
);
I am intending to get snapshot val from Firebase within my React component. I want to get the values based on init of the component and attach a listener for changes.
class ChatMessages extends Component {
constructor(props) {
super(props);
this.state = {
messages: [],
};
this.getMessages = this.getMessages.bind(this);
}
getMessages(event) {
const messagesRef = firebase.database().ref('messages');
messagesRef.on('value', function(snapshot) {
this.setState({ messages: snapshot.val() });
});
}
componentDidMount() {
this.getMessages();
}
render() {
return (
<div className="container">
<ul>
<li>Default Chat Message</li>
{ this.state.messages }
</ul>
</div>
);
}
}
This is because 'this' is losing its context. So that, 'this.setState' is being undefined. You can have a reference for the actual 'this' via a variable called 'that'.
class ChatMessages extends Component {
constructor(props) {
super(props);
this.state = {
messages: [],
};
this.getMessages = this.getMessages.bind(this);
}
getMessages(event) {
const messagesRef = firebase.database().ref('messages');
let that = this
messagesRef.on('value', function(snapshot) {
// here
that.setState({ messages: snapshot.val() });
});
}
componentDidMount() {
this.getMessages();
}
render() {
return (
<div className="container">
<ul>
<li>Default Chat Message</li>
{ this.state.messages }
</ul>
</div>
);
}
}
Or if possible, you can use arrow function, which keeps its context.
getMessages(event) {
const messagesRef = firebase.database().ref('messages');
// here
messagesRef.on('value', snapshot => {
// here
that.setState({ messages: snapshot.val() });
});
}
I have the following:
import React from 'react';
import axios from 'axios';
class FirstName extends React.Component {
constructor(props) {
super(props);
this.state = {
submitted: false
};
}
getName () {
var name = this.refs.firstName.value;
this.setState(function() {
this.props.action(name);
});
}
handleSubmit (e) {
e.preventDefault();
this.setState({ submitted: true }, function() {
this.props.actionID(2);
this.props.activeNav('color');
});
}
render () {
return (
<div>
<h2>tell us your first name</h2>
<form>
<input
type="text"
ref="firstName"
onChange={this.getName.bind(this)}
/>
<div className="buttons-wrapper">
<button href="#">back</button>
<button onClick={this.handleSubmit.bind(this)}>continue</button>
</div>
</form>
</div>
);
}
};
class PickColor extends React.Component {
backToPrevious (e) {
e.preventDefault();
this.props.actionID(1);
this.props.activeNav('name');
}
goToNext (e) {
e.preventDefault();
this.props.actionID(3);
this.props.activeNav('design');
this.props.displayIconsHolder(true);
}
getColorValue(event) {
this.props.color(event.target.getAttribute("data-color"));
}
render () {
var colors = ['red', 'purple', 'yellow', 'green', 'blue'],
colorsLink = [];
colors.forEach(el => {
colorsLink.push(<li
data-color={el}
key={el}
onClick={this.getColorValue.bind(this)}
ref={el}>
{el}
</li>
);
});
return (
<section>
<ul>
{colorsLink}
</ul>
<button onClick={this.backToPrevious.bind(this)}>back</button>
<button onClick={this.goToNext.bind(this)}>continue</button>
</section>
);
}
}
class ConfirmSingleIcon extends React.Component {
goBack () {
this.props.goBack();
}
confirmCaptionandIcon (event) {
var optionID = event.target.getAttribute("data-option-id"),
name = event.target.getAttribute("data-option-name");
this.props.setOptionID(optionID);
this.props.setIcon(1, name, optionID, false);
}
goNext () {
this.props.goNext();
}
render () {
console.log(this.props.currentState);
var options = [],
that = this;
this.props.iconOptionsList.forEach(function(el){
options.push(<li onClick={that.confirmCaptionandIcon.bind(that)} key={el.option} data-option-name={el.option} data-option-id={el.id}>{el.option}</li>);
});
return (
<div>
<h2>Choose your caption</h2>
<h3>
{this.props.selectedIcon}
</h3>
<ul>
{options}
</ul>
<button onClick={this.goBack.bind(this)} >back</button>
<button onClick={this.goNext.bind(this)} >confirm</button>
</div>
);
}
}
class ConfirmCaption extends React.Component {
handleClick () {
var currentState = this.props.currentState;
this.props.setIcon(currentState.icon_ID, currentState.selectedIcon, currentState.option_ID, true);
this.props.setIconVisiblity(true);
this.props.setIconListVisiblity(false);
}
render () {
console.log(this.props.currentState);
return (
<div>
<p onClick={this.handleClick.bind(this)}>confirm icon and caption</p>
</div>
);
}
}
class ChooseIcon extends React.Component {
constructor(props) {
super(props);
this.state = {
icons: [],
iconList: true,
confirmIcon: false,
confirmCaption: false,
selectedIconOptions: '',
icon_ID: '',
option_ID: '',
selectedIcon: ''
};
this.setOptionID = this.setOptionID.bind(this);
this.setIconVisiblity = this.setIconVisiblity.bind(this);
this.setIconListVisiblity = this.setIconListVisiblity.bind(this);
}
setOptionID (id) {
this.setState({ option_ID: id })
}
setIconVisiblity (onOff) {
this.setState({ confirmIcon: onOff })
}
setIconListVisiblity (onOff) {
this.setState({ iconList: onOff })
}
componentDidMount() {
var url = `http://local.tshirt.net/get-options`;
axios.get(url)
.then(res => {
this.setState({ icons:res.data.icons });
});
}
handleClick (event) {
var iconId = event.target.getAttribute("data-icon-id"),
that = this;
this.state.icons.forEach(function(el){
if(el.id == iconId){
that.setState(
{
confirmIcon: true,
iconList: false,
selectedIcon: el.name,
icon_ID: iconId,
selectedIconOptions: el.option
}
);
}
});
}
goBack () {
this.setState(
{
confirmIcon: false,
iconList: true
}
);
}
goNext () {
this.setState(
{
confirmIcon: false,
iconList: false,
confirmCaption: true
}
);
}
render () {
var icons = [];
this.state.icons.forEach(el => {
icons.push(<li data-icon-id={el.id} onClick={this.handleClick.bind(this)} key={el.name}>{el.name}</li>);
});
return (
<div>
{this.state.iconList ? <IconList icons={icons} /> : ''}
{this.state.confirmIcon ? <ConfirmSingleIcon goBack={this.goBack.bind(this)}
goNext={this.goNext.bind(this)}
setIcon={this.props.setIcon}
selectedIcon={this.state.selectedIcon}
iconOptionsList ={this.state.selectedIconOptions}
setOptionID={this.setOptionID}
currentState={this.state} /> : ''}
{this.state.confirmCaption ? <ConfirmCaption currentState={this.state}
setIcon={this.props.setIcon}
setIconVisiblity={this.setIconVisiblity}
setIconListVisiblity={this.setIconListVisiblity} /> : ''}
</div>
);
}
}
class IconList extends React.Component {
render () {
return (
<div>
<h2>Pick your icon</h2>
<ul>
{this.props.icons}
</ul>
</div>
);
}
}
class Forms extends React.Component {
render () {
var form;
switch(this.props.formID) {
case 1:
form = <FirstName action={this.props.action} actionID={this.props.switchComponent} activeNav={this.props.activeNav} />
break;
case 2:
form = <PickColor displayIconsHolder={this.props.seticonsHolder} color={this.props.colorVal} actionID={this.props.switchComponent} activeNav={this.props.activeNav} />
break;
case 3:
form = <ChooseIcon setIcon={this.props.setOptionA} />
break;
}
return (
<section>
{form}
</section>
);
}
}
export default Forms;
"ChooseIcon" is a component that will get used 3 times therefore everytime I get to it I need to bring its state back as if it was the first time.
Ideally I would need to make this ajax call everytime:
componentDidMount() {
var url = `http://local.tshirt.net/get-options`;
axios.get(url)
.then(res => {
this.setState({ icons:res.data.icons });
});
}
is there a way to manually call componentDidMount perhaps from a parent component?
React handles component lifecycle through key attribute. For example:
<ChooseIcon key={this.props.formID} setIcon={this.props.setOptionA} />
So every time your key (it can be anything you like, but unique) is changed component will unmount and mount again, with this you can easily control componentDidMount callback.
If you are using the ChooseIcon component 3 times inside the same parent component, I would suggest you to do the ajax in componentDidMount of the parent component like this (exaclty how you have in your example, in terms of code)
componentDidMount() {
var url = `http://local.tshirt.net/get-options`;
axios.get(url)
.then(res => {
this.setState({ icons:res.data.icons });
});
}
and then pass this data down to the ChooseIcon component
render() {
return (
//do your stuff
<ChooseIcon icons={this.state.icons}/>
)
}
after this you will only need to set the received props in your ChooseIconcomponent, for that you only need to change one line in it's constructor:
constructor(props) {
super(props);
this.state = {
icons: props.icons, // Changed here!
iconList: true,
confirmIcon: false,
confirmCaption: false,
selectedIconOptions: '',
icon_ID: '',
option_ID: '',
selectedIcon: ''
};
this.setOptionID = this.setOptionID.bind(this);
this.setIconVisiblity = this.setIconVisiblity.bind(this);
this.setIconListVisiblity = this.setIconListVisiblity.bind(this);
}
The parent component can use a ref to call the function directly.
However, trying to force this function feels like a smell. Perhaps lifting the state higher up the component tree would solve this problem. This way, the parent component will tell ChooseIcon what to show, and there will not be a need to call componentDidMount again. Also, I assume the Ajax call can also occur once.
I have seen a lot loader plugins that work for the Mount life cycle but none for the update part and I wonder how to handle it?
What I tried was following setup for parent:
class App extends React.Component {
constructor() {
super()
this.state = {loader_wrap:false};
this.hideLoader = this.hideLoader.bind(this);
this.showLoader = this.showLoader.bind(this);
}
hideLoader(){
this.setState({loader_wrap: false});
}
showLoader() {
this.setState({loader_wrap: true});
}
render() {
var loaderStyle;
if (this.state.loader_wrap) {
loaderStyle = {display:"block"};
} else {
loaderStyle = {display:"none"};
}
return (
<div>
<div id="content">
{React.cloneElement(content, {
hideLoader: this.hideLoader,
showLoader: this.showLoader
})}
</div>
<div id="loader-wrap" style={loaderStyle}>
<img className="loader hidden-sm hidden-xs" src='source/file/'>
</div>
</div>
)
}
}
And this is the child calling the methods:
class Childextends React.Component {
constructor() {
super();
this.state = {results:[]};
this.calculate = this.calculate.bind(this);
}
calculate(dict) {
this.props.showLoader();
Actions.action(dict)
.then(results => {
this.setState({results: results});
})
.catch((err) => {
var errResp = JSON.parse(err.response);
console.log(errResp);
this.setState({responseErrors: errResp});
});
}
componentDidMount() {
this.props.hideLoader();
}
componentDidUpdate() {
this.props.hideLoader();
}
componentWillReceiveProps(values){
this.setState({results:values.results});
}
render() {
return (
/*stuff to be returned*/
)
}
}
I also tried to use the Will methods .. which worked even worser :D
Any ideas how to implement this? I use react with flux but don't now how to use it in this case ..
Why not just call hideLoader() in the callback of the action's promise?
class Childextends React.Component {
constructor() {
super();
this.state = {results:[]};
this.calculate = this.calculate.bind(this);
}
calculate(dict) {
this.props.showLoader();
Actions.action(dict)
.then(results => {
this.setState({results: results});
})
.catch((err) => {
var errResp = JSON.parse(err.response);
console.log(errResp);
this.setState({responseErrors: errResp});
})
.then(() => {
this.props.hideLoader();
});
}
render() {
return (
/*stuff to be returned*/
)
}
}
Edit: A different approach to the parent component as well - rather than hiding the element with a style, just don't render it if it isn't required.
render() {
return (
<div>
<div id="content">
{React.cloneElement(content, {
hideLoader: this.hideLoader,
showLoader: this.showLoader
})}
</div>
{this.state.loader_wrap &&
<div id="loader-wrap" style={loaderStyle}>
<img className="loader hidden-sm hidden-xs" src='source/file/'>
</div>
}
</div>
)
}