How to pass parameter to a onClick function in ReactJS - reactjs

in my application, I defined a function click_button_response(param1) which writes the param1 to a DynamoDB table.
Then my app subscribes to an IoT Topic. It will receive a real-time message from a topic. When the button is clicked, the click_button_response(param1) function, passes the url as param1. I hope url can be saved in DynamoDB. However, it gave me the error message:
"Error Error: Pass options.removeUndefinedValues=true to remove undefined values from map/array/set."
I replaced the parameter with a string and the error went away, so I am confident that I passed the parameter in a wrong way. Thank you!
import putItemInDynamoDB from functions.js
const click_button_response = async (param1) => {
putItemInDynamoDB(param1)
}
class Sensors extends React.Component{
constructor(props) {
super(props);
this.state = {
sensorMsg: '{"null": 0}'
};
}
componentDidMount(){
Amplify.PubSub.subscribe(TOPIC).subscribe({
next: data => {
try{
this.setState({ sensorMsg: data.value });
}
catch (error){
console.log("Error, are you sending the correct data?");
}
},
error: error => console.error(error),
close: () => console.log('Done'),
});
}
render(){
const { sensorMsg } = this.state;
const url = sensorMsg['param1'];
return(
<div className="Sensor">
<button onClick={() => click_button_response({url})}>Button1</button>
</div>
)
}
}

if click_button_response expects param1 to be a string you are currently passing it an object. () => click_button_response({url}) when we enclose url in curly braces it actually gets converted into object { url: 'actual url value' }. Change the onClick handler function to this () => click_button_response(url). Also sensorMsg['param1'] is the output of this the expected url string?

Related

How to show messages in a chat like manner

I got an array of chat messages from an API call & I mapped it to a component (shown in code below). In that component I rendered the messages according to a condition set. But .map method is just replacing the previous message with a new one. I want to .concat() these messages & show them one by one like in a chat.
ChatBox Component:
this.state = {
messages: [] }
getNewMessages() {
//api called & got response
this.setState({
messages: parsedResponse });
}
render() {
return(<>
{messages.map(messages => (
<NewMessages data={messages} />))}</>)
}
NewMessages Component: -
`this.state = { message: this.props.data.message }`
`return(<p>{message}</p>)`
How can I concat this array so that I can get all the messages instead of only last one.
Spread operator can help you with it.
getNewMessages() {
//api called & got response
this.setState({
messages: [...this.state.messages, ...parsedResponse]
});
}
And just render the message itself.
render() {
return(
<>
{this.state.messages.map(messages => (<p>{message}</p>))}
</>
)
}
render() {
return messages.forEach(m => `<div>${m}</div>`);
}

How to work with async server calls in Reactjs 16.7.0

What is the correct way to use async methods as props in ReactJs? I have an app which mounts a component "dashboard" and this component then loads two drop down menus. The first drop down is populated by async fetch at the componentDidMount lifecycle method.
When the first drop down changes, the async event handler is triggered and the state property selectedAId is supposed to be set. If I debug this in the browser I do see this state prop set. After the state is set, the same event handler also calls an async server method to get some data based on the state property selectedAId.
If I then go to the async server method getEntitledSites, the parameter sent to the method is "0", instead of the value that I previously saw being set in the event handler.
Here is the gist to a summary of the code:
https://gist.github.com/thehme/c4b5a958ef1a6e5248f697375c9cb84b#file-api-ts-L15
api.ts
class Api {
...
async getEntitledForA() {
try {
const orgsResponse = await fetch('/api/v1/orgs');
const orgResponseJson = await orgsResponse.json();
return orgResponseJson.data;
} catch (err) {
console.error(err);
return null;
}
}
async getEntitledSites(orgId: number) {
try {
const sitesResponse = await fetch('/api/v1/study/' + orgId + '/sites');
const sitesResponseJson = await sitesResponse.json();
return sitesResponseJson;
} catch (err) {
console.error(err);
return null;
}
}
}
export default new Api();
dashboard.jsx
import React, {Component} from 'react';
import SelectDropDownA from '../components/SelectDropDownA';
import SelectDropDownB from '../components/SelectDropDownB';
import SelectDropDownC from '../components/SelectDropDownC';
class CreateDashboard extends Component {
constructor() {
super();
this.state = {
organizations: [],
sites: [],
selectedAId: "0",
selectedBId: "0",
selectedCId: "0",
showBMenu: false
}
}
async componentDidMount() {
let organizations = await Api.getEntitledForA();
this.setState({ organizations });
}
async onSelectedOrgChange(selectedOrgId) {
if (selectedOrgId !== 0) {
this.setState({ selectedAId });
let sitesData = await Api.getEntitledSites(this.state.selectedAId);
this.setState({
sites: sitesData.sites,
showSitesMenu: true
});
}
}
...
render() {
return (
<div className="panel">
<div className="insidePanel">
<SelectStudyDropDown
onChange={e => this.onSelectedOrgChange(e.target.value)}
...
/>
</div>
{this.showSitesMenu &&
<div className="insidePanel">
<SelectSiteDropDown
onChange={e => this.onSelectedSiteChange(e.target.value)}
...
/>
</div>
}
</div>
)
}
}
export default CreateDashboard;
I am wondering if this has to do with how I am using async/await or the binding of the onChange method.
SOLUTION: In my particular case, I do not need this.state to set selectedAId before starting my server request, so by passing the parameter straight to the request, works fine. Then I can simply move the setting of selectedAId to the second instance of this.setState. This would not work, if I needed selectedAId to be set before making the server request, in which case, I would have to wait for this.state to finish being set, but this isn't the case since I want my server request to start right away.
async onSelectedOrgChange(selectedOrgId) {
if (selectedOrgId !== 0) {
let sitesData = await Api.getEntitledSites(selectedOrgId);
this.setState({
selectedAId,
sites: sitesData.sites,
showSitesMenu: true
});
}
}
this.setState is asynchronous and not awaitable (i.e. does not return a promise), so if you want to do something with the new state after it has been set, you need to do it in a callback:
this.setState({ selectedAId }, async () => {
let sitesData = await Api.getEntitledSites(this.state.selectedAId);
this.setState({
sites: sitesData.sites,
showSitesMenu: true
});
});
Additionally, in your example, onSelectedOrgChange isn't binded to this anywhere. However if this was an issue the whole thing would have broken long before so I suppose that you just left it out by accident?
Furthermore I would advise you to extract the e.target.value inside the handler and pass onChange={this.onSelectedOrgChange} as the prop, because in your current code you're creating a new onChange handler every time render is called.

Saving input data on async storage

I have been trying to save input data on async storage but whenever i go back after input, it shows the previous data, not the updated one. Here's the code where i think some changes are needed.
constructor(props) {
super(props)
this.state = {
userData: {
address: ''
}
}
}
saveAddressEdit = (fieldName, fieldValue) => {
AsyncStorage.setItem(userData.address, fieldValue)
}
render() {
const {address} = this.state.userData
return (
<Input placeholder=" Type your Address:"
onChangeText={(address) => this.setState({address})}
value={address} />
<Button full danger onPress={()=>this.saveAddressEdit('address', address)} style={{backgroundColor:'#ff0000'}}>
);
}
If you want to persist you address through the component lifecycle using AsyncStorage then you should first save it properly and once you get back on the screen, fetch it and populate back it into your state, as component's state needs to be reinitialised using your AsyncStorage.
The following code should help initialize the address back to the stored one:
componentDidMount () {
AsyncStorage.getItem('address').then(value => {
if (value) {
this.setState({address: value});
}
})
}
and the below specifies the correct way to set the data to AsyncStorage:
saveAddressEdit = async (fieldName, fieldValue) => {
await AsyncStorage.setItem(fieldName, fieldValue)
}
OR you can also you the promise way just like I did in getItem above like setItem(fieldName, fieldValue).then(value => console.log('saved')) but since we don't need to handle anything in the then so I resist using promise in such places.

function returning data but not showing

I have this component
const SummaryBar = props => {
const { MainObject} = props;
const localGetUserFromID = userID => {
getEmailFromId(userID).then(results => {
return results.data.Title; //Comment: This one returning friendly name
});
};
return (<span>Hello {localGetUserFromID(MainObject.AuthorId)}</span>)
}
but when I render it somehow the its only showing Hello and not the output I am getting from my localGetUserFromID function. Am I doing wrong? Note the AuthorId is being pass to an API and the MainObject came from the App Level,
FYI when I try to debug it using dev tools the function is retuning the text I am look for.
localGetUserFromID() returns nothing, that is, undefined, and that's why you see Hello only.
And because localGetUserFromID() makes an asynchronous call to get an email from user ID, it doesn't have to be in render() method. Now this component is defined as a state-less component, but you can re-define it as a stateful component, call the getEmailFromId() in componentDidMount() life-cycle method, and use a return value as an internal state.
Then you can show a value of the internal state after Hello.
class SummaryBar extends Component {
// Skipping prop type definition.
constructor(props) {
super(props)
this.state = {
username: '',
}
}
componentDidMount() {
const { MainObject: { AuthorId } } = this.props
getEmailFromId(AuthorId).then((results) => {
this.setState({
username: results.data.title,
})
})
}
render() {
const { username } = this.state
return (
<span>
Hello { username }
</span>
)
}
}
When things run when debugging but not when running and you are using promises as you are, the 99% of the times is because promises hasn't been resolved when you print.
localGetUserFromID indeed returns a promise that resolves to the friendly name.
You can just prepend await to localGetUserFromID(MainObject.AuthorId) and rewrite you return as this:
return (<span>Hello {await localGetUserFromID(MainObject.AuthorId)}</span>)

Reactjs Select v2 - How to handle Ajax Typing?

I am using reactjs select 2 but I don't know how to make it work so that when a user types something in a ajax request is made and the results are sent back.
I see it has some async options but I don't get how it works and how I would get it to work with axios.
I come up with this but it is kinda laggy when a user types(probably because it is re-rendering it after each type) and when the user selects a choice the value disappears.
export default class TestComponent extends Component {
constructor(props) {
super(props);
this.state = {value: ""};
}
onInputChange(option) {
this.getOptionsAsync(option)
}
getOptionsAsync(newInput) {
var that = this;
console.log("ffd", newInput)
axios.get(`https://localhost:44343/api/States/GetStatesByText?text=${newInput}`)
.then(function (response) {
var formatedResults = response.data.map((x)=> {
return {value: x.id, label: x.name}
})
that.setState({
options: formatedResults,
value: newInput
})
})
.catch(function (error) {
});
}
render() {
console.log(this.state.value, "value")
return (
<div className="test">
<Select
onInputChange={this.onInputChange.bind(this)}
value={this.state.value}
options={this.state.options }
/>
</div>
);
}
}
You're going to be doing an api call every single time that you type a letter with the current way you're doing things. I would recommend just loading the states once at the beginning, perhaps in your ComponentDidMount() method.
If you pass the isSearchable prop to React-Select it will automatically work as a filter anyways.
Another thing I've had to do in this case which I believe will fix your change problem is to make sure it calls the handler on change not just on input change.
Pass this prop:
<Select
value={this.state.value}
options={this.state.options }
onChange={value => {
if (value) this.onInputChange(value)
else this.onInputChange('')
}
/>
Due to the way this is automatically bound to arrow functions, you won't have to bind to this if you change your onInputChange to the following:
onInputChange = (value) => {
this.getOptionsAsync(value)
}
Finally, you should be setting the state in the above function so the value is stored.
onInputChange = (value) => {
this.getOptionsAsync(value)
this.setState({value})
}

Resources