Using react-select Async with loadOptions and redux-form - forms

I'm using react-select library to display a select box. I'm using Select.Async because I need to pull my options from an API. I use Select with loadOptions and it works during the intial page render. However, I'm also using redux-form which can change the value of a Field dynamically (using change). However, when I change the value of the Field like this, the value of the input does change (and I can verify this), but react-select's loadOptions is never called again (even though I thought it was supposed to be listening to a change of value). My question is, is there a way to dynamicaly call loadOptions every time the input value changes?
Thanks,
Edit: Answered on github here

this.state = {
store: '',
};
this.handleStoreSelect = this.handleStoreSelect.bind(this);
handleStoreSelect = (item) => {
this.setState({
store: item.value
}
};
<Select.Async
name="storeID"
value={this.state.store}
loadOptions={getStores}
onChange={this.handleStoreSelect}
/>
const getStores = () => {
return fetch(
"api to be hit",
{
method: 'get',
headers: {
'Content-Type': 'application/json'
}
}
)
.then(response => {
if(response.status >= 400){
throw new Error("error");
}
return response.json()
})
.then(stores => {
let ret = [];
for(let store of stores) {
ret.push({value: store._id, label: store.name})
}
return {options: ret};
})
.catch(err => {
console.log('could not fetch data');
console.log(err);
return {options: []}
})
};
Using this we can fetch the data and pass this object in the loadoptions.
copy this code outside the class. and also i'm posting the code to be implemented for loadoptions

It might be a better solution than this, but a quick one is to set a ref to your Select.Async component, and when a change action is triggered (like the change of an input - your case, or one button click event - like in the code below) you can update its options. I'm using a similar example with the example of their docs.
class YourClass extends React.Component {
getOptions = (input, callback) => {
setTimeout(function () {
callback(null, {
options: [
{value: 'one', label: 'One'},
{value: 'two', label: 'Two'}
]
});
}, 500);
};
updateOptions = () => {
this.selectAsync.state.options.push(
{value: 'three', label: 'Three'}
)
}
render() {
let props = this.props;
return (
<div>
<Select.Async
ref={selectAsync => this.selectAsync = selectAsync}
loadOptions={this.getOptions}
/>
<button onClick={this.updateOptions}>
Load more items
</button>
</div>
)
}
}

Related

Failed to add new elements when set initialState as an empty object

I try to use redux toolkit and I have this as menu-slice.js
I try to use property accessors to add a new property to fileItems, its initial value is an empty object.
import { createSlice } from "#reduxjs/toolkit";
const menuSlice = createSlice({
name: "ui",
initialState: {
fileItems: {},
},
reducers: {
setFileDate: (state, action) => {
state.FileDate = action.payload;
},
replaceFileItems: (state, action) => {
const filesList = action.payload.map((fileName) =>
fileName.slice(fileName.indexOf("/") + 1)
);
state.fileItems[state.FileDate] = filesList;
console.log(`filesList: ${filesList}`);
console.log(`state.fileItems: ${JSON.stringify(state.fileItems)}`);
console.log(`state.FileDate: ${state.FileDate}`);
state.fileContents = null;
},
I call dispatch with the api return value ( dispatch(menuActions.replaceFileItems(fileResponse.data));)
in menu-action.js:
the return value is an array of strings.
export const fetchFiles = (fileDate) => {
return async (dispatch) => {
const fetchFilesList = async () => {
const response = await fetch(
"some url" +
new URLSearchParams({
env: "https://env.com",
date: fileDate,
})
);
if (!response.ok) {
throw new Error("Fail to fetch files list!");
}
const data = await response.json();
return data;
};
try {
const fileResponse = await fetchFilesList();
dispatch(menuActions.setFileDate(FileDate));
dispatch(menuActions.replaceFileItems(fileResponse.data));
} catch (error) {
dispatch(
menuActions.showNotification({....
})
);
}
};
};
But it never prints console logs and didn't display where went wrong in the console or in the chrome redux extension.
I want to add data into state.fileItems on each click that triggers fetchFiles() when it returns a new array:
from state.fileItems = {}
check if state.fileItems already has the date as key,
if not already has the date as key,
change to ex: state.fileItems = {"2022-01-01": Array(2)}
and so on..
ex: state.fileItems = { "2022-01-01": Array(2), "2022-01-02": Array(2) }
I also tried to set state.fileItems as an empty array, and use push, but it didn't work either, nothing printed out, state.fileItems value was always undefined.
Can anyone please tell me why this didn't work?
Thanks for your time to read my question.

Copy nested objects from Axios response to my React-native hook?

Im simply trying to copy the Nested objects i get back from the axios GET request to my react-native hook. Not straightforward it seems. Data would look something like this for example:
[
{
_id: 61242b08013a5f26bd1b2d47,
user: '6110675d65e1528d03a8bce6',
totalCalories: 7,
totalProtein: 7,
createdAt: 2021-08-23T23:11:04.076Z,
updatedAt: 2021-08-24T00:53:38.621Z,
__v: 0
},
{
_id: 6125990e9669cc6b466c37b5,
user: '6110675d65e1528d03a8bce6',
__v: 0,
createdAt: 2021-08-25T01:12:44.343Z,
totalCalories: 2,
totalProtein: 2,
updatedAt: 2021-08-25T01:14:01.439Z
}
]
However, i get a component exception: undefined is not an object error, as well as a 404 error when trying to access it via historyData in my frontend. Here is my component which renders the history screen in my iOS app:
Frontend:
const History = () => {
const [currentUsersID, setCurrentUsersID] = React.useState("");
const [historyData, setHistoryData] = React.useState();
// Gets the current user's ID from local storage
const getData = async () => {
try {
const value = await AsyncStorage.getItem("#storage_Key");
if (value !== null) {
setCurrentUsersID(value);
}
} catch (error) {
console.log("Error reading AsyncStorage value -> " + error);
}
};
getData();
async function getHistory() {
try {
const response = await axios.get("http://localhost:5000/daysLog/getLog/" + currentUsersID);
setHistoryData(() => {
return response.data;
});
} catch (error) {
console.log("ERROR (getHistory) -> " + error);
}
}
useFocusEffect(
React.useCallback(() => {
getHistory();
})
);
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="light-content" />
<Text style={{ color: "white" }}>
History: {historyData[0].totalCalories} // ERROR HERE
</Text>
</SafeAreaView>
);
};
Backend:
const router = require("express").Router();
let daysLog = require("../models/daysLog.model");
// getting the user's existing daysLogs
router.route("/getLog/:userID").get((req, res) => {
daysLog
.find({
user: req.params.userID,
})
.then((logs) => res.json(logs))
.catch((error) =>
res.status(400).json("Error (dayLog/GET) -> " + error)
);
});
historyData[0].totalCalories is throwing that because it will take time for it to get fetched while you're waiting for a response. You should have a block to test if historyData is not nul before you render the result.
Also get history focus effect relies on currentUser being valid but there's no expression that ensures it the way you wrote it. At best it's a race condition. Consider changing your focus effect to be a regulaf effect and make the currentUserId it's dependency.
Then inside of it to you can check if currentUserId is not null and start fetching get history accordingly.

Vuejs/Posgres - When clicked on button I want to save a value in db postgresql

Hi so I have a view where I have a button , When it's clicked I want a value to be saved in the db . What I get now is nothing like I click on button but nothing happens .
Here's the code I have :
<a-button type="primary" class="mb-4 text-center mr-1 float-right" #click="onSubmit">Confirm</a-button>
in my script I have:
setup(){
const onSubmit = () => {
axios.post("/insertstatut/"+876,"added").then((res)=>{
message.success(`statut ajouté`)
router.push({
path:'/cand',
}).catch(error => {
console.log('error', error);
})
} ,
)
}
}
Please if u have any idea what I should do , do share thank you.
you are using composition api feature setup in your vue code,
you need to return the methods or properties u wish to use in in your template.
setup () {
return {
onSubmit: () => {}, //some method u want to use later in template ,
show: false, // or some property
}
}
this is how your component should look
<template>
<a-button
type="primary"
class="mb-4
text-center
mr-1float-right"
#click="onSubmit"
>
Confirm
</a-button>
</template>
<script>
import AButton from './button-path/AButton.vue'
import axios from 'axios'
export default {
componets: { AButton },
setup() {
const onSubmit = () => {
axios.post('/insertstatut/' + 876, 'added').then((res) => {
message.success(`statut ajouté`)
router
.push({
path: '/cand',
})
.catch((error) => {
console.log('error', error)
})
})
}
// Expose your constants/methods/functions
return {
onSubmit,
}
},
}
</script>

How to use formatter method with async call

im try to display some data using b-table and the formatter method using axios with the spread method but this its not displaying correctly.
this is what i have https://codepen.io/damian-garrido/pen/MWgxqeZ
html template
<div id="app">
<b-table
:fields="fields"
:items="items">
</b-table>
</div>
js file
window.onload = () => {
new Vue({
el: '#app',
data() {
return {
fields: [
{
key: 'owner',
label: 'Poke Owner'
},
{
key: 'pokemonIds',
label: 'Poke Name',
formatter: 'getPokeName'
}
],
items: [
{
owner: 'Juan',
pokemonIds: [3,4]
},
{
owner: 'Diego',
pokemonIds: [7,9,14]
}
]
}
},
methods: {
getPokeName: function (pokeIds) {
let promises = []
for (let id of pokeIds) {
promises.push(axios.get(`https://pokeapi.co/api/v2/pokemon/${id}`))
}
axios.all(promises)
.then( axios.spread((...responses) => {
let names = ''
for (let r of responses) {
names += r.data.name + ', '
}
console.log(names)
return names
}))
}
}
})
}
the console.log return the names, as i need, but not display this on the table.
Table formatter methods must be synchronous.
You would need to make your formatter an async method that uses await to return the value from your formatter. Note that this will slow your app rendering to a crawl as each row will have to wait for the async call to finish before it can render the table row.
Your best bet would be to do the lookup processing in your app, and then pass that data to the table.

Trying to display a values from multiple API fetch

Currently I'm reading some stock/share names from my DB (Mongo) and trying to display their price by calling an API for each one of them on my component, but I'm getting only the last one.
HTML
<div class="posts-container">
<div class="post" v-for="post in posts"
v-bind:key="post['2. Symbol'] " >
<p class="text">{{ post['2. Symbol'] }} </p>
</div>
</div>
JS
data(){
return {
posts: [],
error: '',
text: ''
}
},
async created() {
try {
const acoes = await APIService.getPosts();
acoes.map(data => {
fetch(`https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=${data.name}.SA&interval=15min&outputsize=compact`)
.then(res => res.json())
.then(data => {
this.posts = { ...this.posts, ...data}
});
})
console.log(this.posts);
} catch (err) {
this.error = err.message;
}
},
So as I have multiple values on my const acoes I expected to generate data for each one of them, but what I got is just for the last object.
What am I missing here?
There's a smarter way of doing this instead of calling the API on the component?
You can use Promise.all to combine all your result.
const allFetches = acoes.map(
data => fetch(...)
.then(response => response.json()
)
Promise.all(allFetches)
.flatMap(result => result)
.then(posts => {
this.posts = posts;
});