I'm currently building a Hacker News Reading Client in React Native using their Official API
I want to display the top stories of the day. So first I get the id's of the top posts for the day and then use those id's to fetch the stories.
But when I run this the promise gets rejected with ids.map is undefined.
How can I fix this ?
FeedScreen.js
import { getTopStoriesID, getStoriesByID } from '../../hacknews-api';
class FeedScreen extends Component {
constructor(props) {
super(props);
this.state = {
loaded: false,
stories: [],
};
}
async componentDidMount() {
const storiesID = await getTopStoriesID();
const stories = await getStoriesByID(storiesID[10]);
this.setState({ stories });
}
render() {
return <Text>{this.state.stories}</Text>;
}
}
export default FeedScreen;
hacknews-api.js
const BASE_URL = 'https://hacker-news.firebaseio.com/v0';
export const getTopStoriesID = async () => {
const res = await fetch(BASE_URL + '/topstories.json');
const storiesID = await res.json();
return storiesID;
};
export const getStoriesByID = async (ids) => {
const res = await Promise.all(ids.map(async (id) => {
const res = await fetch(BASE_URL + `/item/{id}.json`)
const story = await res.json();
return story;
}));
const stories = await res.json();
return stories;
}
You can use Array.prototype.map() on an array and not an object.
The map() method creates a new array with the results of calling a
provided function on every element in the calling array.
Example
export const getStoriesByID = async (ids) => {
// check if the argument is defined and its an Array
if(ids && ids.constructor === Array) {
// do something with array of ids, for example do a map()
} else {
// do something otherwise
}
}
Related
Parent compoment:
const { getGroup, removePubli, removeTablet, loadTotal, error } = useGroups();
const group = ref({});
getGroup(group_id).then((g) => {
group.value = g;
});
Child component:
const { group } = defineProps({
group: {
type: Object,
default: {},
},
});
Given the prop above, which is fetched by an async axios call, when I try to use it in a chield component it is undefined or a promise.
I've already tried to put an async inside onMounted hook but it doesn't work.
getGroup does it:
const getGroup = async (id) => {
const r = await api.get(`/api/v1/groups/${id}`);
if (r?.status !== 200) error.value = "Falhou";
return r.data;
};
I want to await it before the code continues or find a way to do something when the promise resolves itself.
I don't see in your code any await. Use await instead of .then!
<script setup>
const { getGroup, removePubli, removeTablet, loadTotal, error } = useGroups();
const group = ref({})
const result = await getGroup(someId)
group.value = result.data // or what ever
</script>
GamePage.vue
Destructured the pinia state elements and action method
const $store = useGameStore();
const {game, teamOne, teamTwo} = storeToRefs($store);
const { getGame } = $store;
Passed the destructed variables to components
<player-stat-table
:title="teamTwo.name"
:players="teamTwo.players"
:teamColor="teamTwo.team_color"
/>
Table Display
store/game_store.js
I am trying to edit data from the above table using updatePlayer action, after successfully completing the action I am updating the entire store data by recalling the get action method. But the data in the table is not updating reactively, it's updating after page reload. How to update it reactively?
import { api } from 'boot/axios'
import { defineStore } from 'pinia'
import { splitPlayers } from 'src/helpers'
export const useGameStore = defineStore('game', {
state: () => ({
game: null,
teamOne: null,
teamTwo: null,
}),
getters: {
getTeamOne: state => state.teamOne,
getTeamTwo: state => state.teamTwo,
getGameData: state => state.game,
},
actions: {
getGame(payload) {
return new Promise((resolve, reject) => {
api.get(`/games/${payload.gameID}/`)
.then(resp => {
const data = resp.data;
const teams = splitPlayers(data)
this.game = data
this.teamOne = teams[0]
this.teamTwo = teams[1]
resolve(data)
})
})
},
updatePlayer(payload) {
return new Promise((resolve, reject) => {
api.put(`/playerstat/${payload.id}/`, data)
.then(resp => {
const data = resp.data;
this.getGame({gameID: data.game})
resolve(data)
})
})
},
}
})
First, you can get rid of you getters, cause due to pinia documentation,
as getters you can think of as the computed properties
and you're not computing anything. So you can simply access the state properties, what you are already doing in your GamePage.vue file.
Secondly, you should also consider async/await pattern instead of Promiste.then(). Like mentioned in the comments, there's a problem with promise constructor antipattern in the OP.
I also prefer writing my pinia stores with the setup() approach, because I think it fits the vue3/composition-api approach a bit better.
import { api } from 'boot/axios'
import { defineStore } from 'pinia'
import { splitPlayers } from 'src/helpers'
export const useGameStore = defineStore('game', () => {
const game = ref(null);
const teamOne = ref(null);
const teamTwo = ref(null);
const getGame = async (gameId) => {
const resp = await api.get(`/games/${gameId}/`);
const teams = splitPlayers(resp.data)
game.value = resp.data
teamOne.value = teams[0]
teamTwo.value = teams[1]
};
const updatePlayer = async (data) => {
const resp = await api.put(`/playerstat/${data.id}/`, data)
const gameId = resp.data.game;
await getGame(gameId)
};
return {
game,
teamOne,
teamTwo,
getGame,
updatePlayer
}
});
This may be a basic question, but I'm new to React Native and stuck here.
My code pasted below. reducer and functional component. I want to capture the response returned from reducer.
reducer.js
export const ActivationCenterReducer = (
state = INIT_KIT_STATE,
{ type, payload = {} }
) => {
switch (type) {
case 'KIT_ACTIVATION_SUCCESS_DATA': {
const { message, response_code, apiLoading, apiError } = payload;
return {
...state,
apiLoading: apiLoading,
apiError: apiError,
message: message,
response_code: response_code
};
}
// ...
}
// ...
};
Functional Component class:
const kitActivationCenter = ({ route, navigation }) => {
const response_code = useSelector(
store => store.kitActivationCenter.response_code
);
const handleKitActivation = () => {
/*This will call the validation() inside action.js and that follows the reducer.js file. where reducer.js file returning the values on success response. but I am not able to access that response_code returned from reducer.
How to save the response_code from the below dispatch function.*/
dispatch(Validation(locator, pin));
if (response_code === 200) {
// should navigate to the next screen
}
};
};
My question is how to capture the returned response_code from reducer.
I'm able to navigate to the next screen on clicking the submit button couple of times.I notice that first time when the dispatch function is called, the state of the response_code is not updating , hence the response_code != 200.
I want a way to capture the response and assign to variable.
Thanks in advance.
You are probably looking at the old value of response_code in your handleKitActivation.
const kitActivationCenter = ({ route, navigation }) => {
const response_code = useSelector(
store => store.kitActivationCenter.response_code
);
const handleKitActivation = () => {
dispatch(Validation(locator, pin));
// HERE the response_code does not have result value
// of your calling dispatch(Validation(locator, pin)) above yet
if (response_code === 200) {
// should navigate to the next screen
}
};
};
I suggest to move your response_code hanfling to the useEffect:
const kitActivationCenter = ({ route, navigation }) => {
const response_code = useSelector(
store => store.kitActivationCenter.response_code
);
// this effect will run whenever your response_code changes
useEffect(() => {
if (response_code === 200) {
// should navigate to the next screen
}
}, [response_code]);
const handleKitActivation = () => {
dispatch(Validation(locator, pin));
};
};
I've researched this and not found a similar case answered, at my whits end.
I have the following code to get an item from AsyncStorage, which returns the correct value when I log it on the console, but the function's return value is: {\"_40\":0,\"_65\":1,\"_55\":\"Nick\",\"_72\":null}
const getData = async () => {
try {
const value = await AsyncStorage.getItem('userDetails');
if(value !== null) {
const wsi_user = await JSON.parse(value);
console.log("userName: " + wsi_user.userName); // returns "Nick"
return wsi_user.userName; // returns {\"_40\":0,\"_65\":1,\"_55\":\"Nick\",\"_72\":null}
}
} catch(e) {
letter = "D";
}
}
I've seen similar articles where people mention that the promise needs to resolve and I get that, but my result is within the outputted weird object, AND, my result is correct when logged to console the line before return.
Please advise. No clue how to fix this.
You can call this function like:
const result = await getData()
Also, you don't need the await before JSON.parse() as it is synchronous.
Maybe you can use react-native-easy-app, through which you can access AsyncStorage synchronously, and can also store and retrieve objects, strings or Boolean data
import { XStorage } from 'react-native-easy-app';
import { AsyncStorage } from 'react-native';
// or import AsyncStorage from '#react-native-community/async-storage';
export const RNStorage = { userInfo: undefined };
const initCallback = () => {
// From now on, you can write or read the variables in RNStorage synchronously
// equal to [ await AsyncStorage.setItem('userInfo',JSON.stringify({ name:'rufeng', age:30})) ]
RNStorage.userInfo = {name: 'rufeng', age: 30};
};
XStorage.initStorage(RNStorage, AsyncStorage, initCallback);
You need to convert the object to a string before saving it in the async storage AsyncStorage.setItem(key, JSON.stringify(data));
Example:
export const setStorageItem = async (key: string, data: any): Promise<void> => {
return AsyncStorage.setItem(key, JSON.stringify(data));
};
export const getStorageItem = async <T>(key: string): Promise<T | null> => {
const item = await AsyncStorage.getItem(key);
if (item) {
return JSON.parse(item);
}
return null;
};
Example from the docs
I am trying to build an app in react native that is suppose to take take two inputs by a user and then make a query to an api and get information about the two inputs. I have been having trouble with redux and redux-thunk and specifically with async actions.
This is the code in my app that i am specifically having trouble with
export const fetchData = url => {
console.log("start Fetching");
return async dispatch => { // this is where the problem is
dispatch(fetchingRequest());
try {
const response = await fetch("https://randomuser.me/api/?results=10");
const json = await response.text();
if (response.ok) {
dispatch(fetchingSuccess(json));
console.log("JSON", json);
} else {
console.log("fetch did not resolve");
}
} catch (error) {
dispatch(fetchingFailure(error));
}
};
console.log("Fetched data");
};
Upon debugging the function, I have ended with finding that when the fetchData function is called the function will execute but the async dispatch that is being returned has undefined behavior.
The output in the debugger when the function is called should be
start Fetching
JSON file information/Error
but the output in the debugger is actually
start Fetching
This is the function in which fetchData is called in
_onPress = () => {
let url = "https://randomuser.me/api/?results=10";
fetchData(url);
console.log("should have fetched");
};
this is the mapDispatchToProps function that I have added. The problem is i do not know what to add inside the function.
const mapStatetoDispatch = (url, dispatch) => {
return {dispatch(fetchData(url))}; // do not know what to place in body of function
};
i have connected it in the component with
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
these are the action creators that I import, if needed
import {
fetchingSuccess,
fetchingRequest,
fetchingFailure,
fetchData
} from "../data/redux/actions/appActions.js";
Assuming you have added redux-thunk as a middleware, it looks like the errors are here:
_onPress = () => {
const { fetchData } = this.props;
let url = "https://randomuser.me/api/?results=10";
fetchData(url);
console.log("should have fetched");
};
and
const mapStatetoDispatch = dispatch => ({
fetchData: url => dispatch(fetchData(url)),
}};