I am trying to use the search api of Open Library.
Usually, if you are going to use a functional component, you will do it like this on your api file:
import axios from 'axios';
export default axios.create({
baseuRL: 'http://openlibrary.org/search.json'
})
And then you will import this on a file where you will fetch the data:
import booksAPI from '../apis/books';
const books = () => {
useEffect(() => {
books()
}, [])
const books = async() => {
const res = await booksAPI.get('?author=tolkien');
console.log(res.data);
}
}
This is expected to console.log the data on your terminal. However, using the class component with axios and componentDidMount to fetch the data.
class BookList extends Component {
constructor(props) {
super(props)
this.state = {
books: []
}
}
componentDidMount(){
const booksResponse = async() => {
const response = await booksAPI.get('?author=tolkien');
console.log(response.data)
}
}
This is complaining about the await keyword and doesn't console.log the data. Also, I am not sure how I can convert the useEffect to a class component so it can perform side effects?
const BookList = () => {
// State variable where you can store the data
const [books, setBooks] = useState([]);
// Effect, which would be called on component mount because of empty array of dependencies -> [] (look for useEffect lifecycles) and set the data to state variable. After this, component will re-render.
useEffect(() => {
const fetchBooks = async () => {
const response = await booksAPI.get('?author=tolkien');
setBooks(response.data);
console.log(response.data);
}
fetchBooks();
}, []);
return ...
}
Also, make sure that you're adding query/mutation/subscription to your GraphQL document right before the name of query/mutation/subscription, as someones told you in the comment.
In case you're looking for class component realization, your code should look like this:
class BookList extends Component {
constructor(props) {
super(props)
this.state = {
books: []
}
}
fetchBooks = () => {
const response = booksAPI.get('?author=tolkien');
this.setState({ books: response.data });
}
componentDidMount(){
fetchBooks();
}
...
}
Here I have created a simple example in React for you.
componentDidMount() {
this.booksResponse();
}
booksResponse = async () => {
const response = await axios.get(
'https://openlibrary.org/search.json?author=token'
);
console.log(response.data);
};
Here is Stackblitz link.
https://stackblitz.com/edit/react-zk8nhq
If you find any confusing then Please comment here I can create more example for you.
Related
I have a two Api's which gets some source and another to add sources.
While displaying the source(images ..) on the screen, in the mean time I am adding some new sources. Since componentDidMount runs only at the start, I can not force componentDidMount to run again whenever a new source added
Here the part of the code:
App.tsx:
export class App extends React.Component<MyProps, MyState> {
constructor(props: any) {
super(props)
this.state = {
sources: [],
currentImage: -1,
}
}
async getSources() {
let allData = await axios.get('/playlist')
return allData.data.data // getting allData from the Api
}
async componentDidMount() {
const sources = await this.getSources()
this.setState(
{
sources: sources,
currentImage: 0,
}
)
}
render() {
<div>
<Playlist onChange={() => this.getSources()} />
.
.
.
</div>
}
}
Playlist.tsx
export function Playlist(props: {onChange: () => void}) {
const {register, handleSubmit} = useForm()
const [data, setData] = useState('')
const onSubmit = async (data: any) => {
console.log('data', await props.onChange())
const url = '/add'
try {
await axios.post(url, data)
await props.onChange(). // trying to call the function after adding new source
} catch (error) {
console.log('Error')
}
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
...
</form>
)
}
As seen in the code, I try to call the function inside the componentDidMount again(after new source is added) to update sources but seems it doesn't work.
I have a variable that I want to keep track of and update its value between two classes. In one of my classes, I started using props like this with the variable isLoading in my Post class:
class Post extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false
};
}
post = () => {
this.props.uploadPost()
this.props.updatePhoto()
this.props.updateDescription('')
this.props.navigation.navigate('Home')
}
openLibrary = async () => {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL)
if (status === 'granted') {
const image = await ImagePicker.launchImageLibraryAsync()
if(!image.cancelled ){
this.setState({ isLoading: true });
const resize = await ImageManipulator.manipulateAsync(image.uri, [], { format: 'jpeg', compress: 0.1 })
const url = await this.props.uploadPhoto(resize.uri)
this.props.updatePhoto(url)
this.setState({ isLoading: false });
}
}
}
...
Now, I also have another class called Camera that I want to update this same variable. However, I'm not implementing a child like function where I call Post or Camera class in each other.
This is my code for Camera.
class CameraUpload extends React.Component {
state = {
type: Camera.Constants.Type.back,
};
snapPhoto = async () => {
const { status } = await Camera.requestPermissionsAsync();
if (status === 'granted') {
const image = await this.camera.takePictureAsync()
global.config.loading = true;
image ? this.props.navigation.navigate('Post') : null
if( !image.cancelled ){
const resize = await ImageManipulator.manipulateAsync(image.uri, [], { format: 'jpeg', compress: 0.1 })
const url = await this.props.uploadPhoto(resize.uri)
this.props.updatePhoto(url)
loading = false;
// url ? this.props.navigation.navigate('Post') : null
}
}
}
I tried using a global config variable but the variable's value was not getting updated between classes. Please let me know what the best way to go about solving this problem is. Thanks!
React Context
You can use the concept of "Context" in react. You may read about it here
https://reactjs.org/docs/context.html
Async Storage
Also you can use async storage if it suits your design
You can make one utility class:
import AsyncStorage from '#react-native-community/async-storage';
export async function storeData(key, value) {
try {
await AsyncStorage.setItem(key, value)
} catch (e) {
console.log("Error Storing in AsyncStorage stroing " + key + " in async Storage")
}
}
export async function getData(key) {
try {
const value = await AsyncStorage.getItem(key)
return (value == null || value == undefined) ? undefined : value
} catch (e) {
console.log("Error Reading in AsyncStorage stroing " + key + " in async Storage")
}
}
You can store and get data through key-value pairs.
// Firstly import it in your js
import * as asyncStorage from './AsyncStorage'
//For storingthe data:
await asyncStorage.storeData("key1", "value1");
// For getting the data:
await asyncStorage.getData("key1")
You can try global variable:
class post {
onpost(){
global.isLoading = true;
}
}
class cameraupload {
componentDidMount(){
global.isLoading = false;
}
}
Context is the way to go for small pieces of shared state, in my opinion.
Combined with hooks, it's very easy to access state and call functions from any child component.
Define your provider with any shared state and functions
import React, { createContext, useState } from 'react'
const Context = createContext()
const MySharedStateProvider = ({ children }) => {
const [myState, setMyState] = useState('hello')
const updateMyState = value => setMyState(value)
return (
<Context.Provider value={{ myState, updateMyState }}>
{children}
</Context.Provider>
)
}
export { MySharedStateProvider, Context as MySharedStateContext }
Create a hook for your Context, that can be used in any child component
import { useContext } from 'react'
import { MySharedStateContext } from './MySharedStateProvider.js'
export const useMySharedState = () => useContext(MySharedStateContext)
Wrap your components with the provider (I know it wouldn't look like this, it's just an example)
<MySharedStateProvider>
<Posts/>
<CameraUpload/>
</MySharedStateProvider>
Now in your Post or CameraUpload component, you use your hook to get the values
import { useMySharedState } from './MySharedStateHook.js'
const Post = () => {
const { myState, setMyState } = useMySharedState()
}
I have written a method like below but i am noticing my API is getting called multiple times.
I am calling my API inside componentDidMount().
class User extends Component {
state = {
cardData: [],
};
userDetails = async () => {
const data = await fetchUserDetails();
if (data) {
const url = data.baseUrl;
const getDetails = Object.values(data).map((users) => users.user);
this.setState({ cardData: getDetails });
}
};
componentDidMount() {
this.userDetails();
}
}
How can i overcome this?
The way I cache data in Class component is like below :
1. make async API call in componentDidMount
2. get API response and dispatch data through redux
3. use response data by mapping state to prop
What I want to do is caching data right after you get API response with mapped redux state value
inside of useEffect in function component
(
It works on class component. but I'm wondering how should I make it work in function component)
export class MyClassComponent extends React.Component {
private readonly _myCachedData = {
'data1': {}
}
public async componentDidMount() {
await this.loadAsyncData();
}
private loadAsyncData() {
const { makeAPICall } = this.props
await makeAPICall();
return this._myCachedData.data1 = this.props.data1FromReduxConnect;
}
}
export const mapStateTopProps = (state) => {
const { data1FromReduxConnect } = state;
return data1FromReduxConnect;
}
...
What I have tried :
export const MyFunctionComponent = props => {
const { data1FromReduxConnect } = props;
const myCachedData = React.useRef();
const loadAsyncData = () => {
const { makeAPICall } = this.props
await makeAPICall();
return myCachedData.current = data1FromReduxConnect;
}
React.useEffect(()=> {
await loadAsyncData();
})
}
export const mapStateTopProps = (state) => {
const { data1FromReduxConnect } = state;
return data1FromReduxConnect;
}
I was only able to get the previous value ofdata1FromReduxConnect unlike class component did get updated value this.props.data1FromReduxConnect after API call
Not sure if I should just keep class component for it, or is there a way to deal with this issue!
Thanks!!
I don't think that is the right way to use the useRef hook. Similar to React's class components' createRef(), it is actually used to access the DOM in functional components.
If the HTTP request happens only once when MyFunctionComponent is initialised, we can use [] as the second argument in the useEffect hook which will cause this effect to be run only once. In addition, we will need to make use of useState hook to keep track of the component's state which is to be updated with the values from the redux store.
export const MyFunctionComponent = props => {
const { data1FromReduxConnect } = props;
const [ myData, setMyData ] = useState();
const loadAsyncData = async() => {
const { makeAPICall } = this.props
await makeAPICall();
}
useEffect(()=> {
async function getData() {
await loadAsyncData();
}
getData();
// do the rest to get and store data from redux
setMyData(data1FromReduxConnect);
}, [])
}
The question comes from a issue where I need data binding and save it to a reducer so I can use pusher to modify the data when needed and it changes in real-time. The problems I find are that:
I am new in react and I don't really know much about how to bind data variables.
I am using remote data with fetch so the data can be refreshed but it can't seem to find a way to properly bind or even save it to a reducer.
Below, the relevant code:
class MainTable extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<MaterialTable
tableRef={this.tableRef}
columns={columnsSetup}
options={materialTableOptions}
data={query =>
new Promise((resolve, reject) => {
pageQuery = query.page;
pageSizeQuery = query.pageSize;
let url = GET_ORDERS_URL;
url += 'qtt=' + pageSizeQuery;
url += '&page=' + pageQuery;
fetch(url)
.then(response => response.json())
.then(result => {
resolve({
data: result.data,
page: result.page,
totalCount: result.totalElems
});
});
})
}
/>
);
}
}
Data fetching should be done in one of the react lifecycle methods. These are build-in functions that will be called on specific "life" events of your component, for example when it gets mounted (componentDidMount).
You should read the docs thoroughly to really get the hang of it.
To give you an example of the implementation of a lifecycle method, I fixed your code below.
A couple of important, but also opinionated, subjects to look into are: lifecycle methods, state, Async/await (instead of promises), Components: classes and hooks.
class MainTable extends React.Component {
state = {
data: {}
};
componentDidMount() {
this.fetchData();
}
fetchData = async query => {
let url = `${GET_ORDERS_URL}qtt=${query.pageSize}&page=${query.page}`;
const response = await fetch(url);
const result = response.json();
this.setState({
data: {
data: result.data,
page: result.page,
totalCount: result.totalElems
}
});
};
render() {
return (
<MaterialTable
tableRef={this.tableRef}
columns={columnsSetup}
options={materialTableOptions}
data={this.state.data}
/>
);
}
}
Below is a functional component (exactly the same logic as above) that uses hooks:
import React, { useEffect, useState } from 'react';
function MainTable() {
const [data, setData] = useState({});
useEffect(() => {
const fetchData = async query => {
let url = `${GET_ORDERS_URL}qtt=${query.pageSize}&page=${query.page}`;
const response = await fetch(url);
const result = response.json();
setData({
data: result.data,
page: result.page,
totalCount: result.totalElems
});
};
fetchData();
}, [data]);
return (
<MaterialTable
columns={columnsSetup}
options={materialTableOptions}
data={data}
/>
);
}
Material-table is a fantastic out the box soloution for tables however, I don't particularity like the way the pagination is handled. I use await and async operators rather than promises and wanted my component to be responsible for managing the state rather than these promise methods that material-table wants you to provide. Material-table provide a way to completely override their pagination logic so this is what I did.
material-table's Pagination component appears to just be a wrapper Material UI's TablePagination component.
const [data,setData] = React.useState([])
const [pagination, setPagination] = React.useState({take: 20, page: 0})
const [count, setCount] = React.useState(0)
React.useEffect(() => {
async function fetch() {
var {result, count} = await someAsyncCallToServer()
setData(fetch)
setCount(count)
}
fetch()
}
,[pagination])
const handleChangePage = (event, newPage) => {
setPagination(old => ({...old, page: newPage}))
}
...
<MaterialTable
data={data}
...
components = {{
Pagination : props =>
<TablePagination
rowsPerPage={pagination.take}
onChangeRowsPerPage={take => setPagination(old=> ({...old, take}))}
page={pagination.page}
count={count}
onChangePage={handleChangePage}
/>
}}
/>