How to update react es6 component after response from ajax call? - javascript

I am new to react and es6 and trying to create search field which fetch some data on type if user type minimum 3 chars on field its make ajax call using fetch api but I am not getting json data when I am running fetch snippet code in browser console its showing json data. whats wrong I am doing in my code. if I get data then how to populate search list I want to know what is the best way to update the component once received data. in below code I have created sub component where I have one prop called items I will update the prop through state is this right way to re-render the react component?
import React from 'react';
import SearchList from "./searchlist"
class SearchField extends React.Component {
constructor(props) {
super(props);
this.state = {SearchText:null,SearchData:{},KeyState:false, items:[]};
};
GetLocationData(){
fetch("http://jsonplaceholder.typicode.com/albums")
.then( (response) => {
return response.json() })
.then((json) => {
return json;
});
};
ChangeHandler(e){
e.preventDefault();
let isKeyUp = this.state.KeyState,
SearchFieldLength = e.target.value,
KeyLength = this.props.KeyRefresh;
if(!isKeyUp && SearchFieldLength.length > KeyLength){
let jsonData = this.GetLocationData();
//this.setState({SearchData:jsonData,KeyState:true})
console.log(jsonData);
}
};
componentDidMount(){
};
render() {
let PlaceholderText = this.props.PlaceHolderText;
return (
<div className="input-text-area">
<input type="text" placeholder={PlaceholderText} onChange={this.ChangeHandler.bind(this)} />
<SearchList items={this.state.items} />
</div>
);
}
}
export default SearchField;

use componentDidMount
componentDidMount {
this.GetLocationData();
}
You should use compnentDidMountand call the GetLocationData() from there. You will need to update your state in the GetLocationData() method which will then call the render method for you automatically.
GetLocationData(){
fetch("http://jsonplaceholder.typicode.com/albums")
.then( (response) => {
return response.json() })
.then((json) => {
this.setState({
items: json
})
return json;
});
};

Related

when I type story in my textbox it should hit this api

when I type story in my textbox it should hit this api
https://hn.algolia.com/api/v1/search?tags=story
data should be shown in the browser.
but right now the call is not happening.
all the relevant code is in searchbar.js
can you tell me how to fix it.
providing my code snippet and sandbox below.
https://codesandbox.io/s/adoring-swanson-ioy2u
class SearchBar extends Component {
async searchByKeyword({ target }) {
await this.getQuery("story", target.value);
}
async componentDidMount() {
await this.getQuery("story", "butts");
}
getQuery = async (type = "", search_tag = "") => {
var url = "https://hn.algolia.com/api/v1/search?tags=";
const resp = await fetch(`${url}${type}&query=${search_tag}`);
return resp.json();
};
render() {
return <input type="text" onChange={this.searchByKeyword} />;
}
}
I've updated your sandbox.
Your searchByKeyword method wasn't bound to SearchBar component which caused this to be undefined inside the method. By adding this code is working:
constructor(props) {
super(props);
this.searchByKeyword = this.searchByKeyword.bind(this);
}
On each data fetch your data is sent to onFetch prop of SearchBar component.
This data is handled by your App component:
<SearchBar onFetch={this.onFetch} />
where data is set to state. That state is represented below search input.

Data from API rendering as empty (array/object issue)

I have a components that aims to display data from API:
class Item extends Component {
constructor(props) {
super(props);
this.state = {
output: []
}
}
componentDidMount() {
fetch('http://localhost:3005/products/157963')
.then(response => response.json())
.then(data => this.setState({ output: data }));
}
render() {
console.log(this.state.output);
return (
<ItemPanel>
<ItemBox>
<BoxTitle>{this.state.output}</BoxTitle>
</ItemPanel>
);
}
export default Item;
console.log(this.state.output) returns proper data, while my component won't render, its throwing this error:
Objects are not valid as a React child (found: object with keys {id, general, brand, images}). If you meant to render a collection of children, use an array instead.
I guess the data from api is an object. I have tried using JSON.parse or toString() already :/
This is output from console:
It seems like you are displaying whole object in <BoxTitle>, Let's try to show brand here. I have update the code given in question.
Updated initial state output [] to {}
class Item extends Component {
constructor(props) {
super(props);
this.state = {
output: {}
}
}
componentDidMount() {
fetch('http://localhost:3005/products/157963')
.then(response => response.json())
.then(data => this.setState({ output: data }));
}
render() {
console.log(this.state.output);
const { brand = {name : ""} } = this.state.output // added default name until we get actual value
return (
<ItemPanel>
<ItemBox>
<BoxTitle>{brand.name}</BoxTitle>
</ItemPanel>
);
}
export default Item;
While your component fetches from your API it renders the element tree described in the render function.
Initial state for output is an empty array and that's a good start.
You should consider what to display to your application user when loading data from the API or if the network request fails.
I'm quite sure that you don't want to render the object returned upon a successful API call as-is.
That said, JSON.stringify function can be used for viewing what result is set in state upon a successful API call before picking what fields will be displayed, how and where they are displayed.
you can better use conditional rendering,
render() {
console.log(this.state.output);
return (
<ItemPanel>
<ItemBox>
{
if(this.state.output.brand.name !=== null) ?
<BoxTitle>{this.state.output.brand.name}</BoxTitle> :
<BoxTitle>Brand Name is Empty</BoxTitle>
}
</ItemPanel>
);
}

async rednering in react

i have a problem where one of my components has to await to get some data while its rendering but i can't find a way to do that.
so i have the render method
render() {
const getComponentProps = async () => {
return await this.props.Store.getComponentProperties(id);
};
componentProps = getComponentProps(id);
return <MyComponent
.
.
data={componentProps}/>;
}
the problem is that my component is rendering before the data is fetched. i can't make the whole render await, i also tried making the componentProps a state on the hope it would rerender once it's ready, but that also didn't work. and finally i tried the new Suspense/Lazy feature in the new react version, which also didn't work.
the data i'm fetching is making a REST call to my database and I have to await it. also, the render it mapping over a list of components not just one, and for each component is has to get its properties and load them.
any thoughts on how to make this async data fetch in render ???
To achieve this, you will have to use your state.
When your component is mounted, you can call the function setState and wait for your data to be fetched. Once the data is fetched, your component will re-render with the correct data :
class MyComponent extends Component {
constructor(props) {
super(props)
this.state = {
data: null
}
}
componentDidMount = async() => {
this.setState({ data: await this.props.Store.getComponentProperties(id) })
}
render() {
return <MyComponent2 data={this.state.data} />;
}
}
If you do not want your child component to receive empty data, you can choose to not render it while your data has not been fetched :
render() {
return this.state.data ? <MyComponent data={this.state.data} /> : <div/>
}

How to make an API call on props change?

I'm creating a hackernews-clone using this API
This is my component structure
-main
|--menubar
|--articles
|--searchbar
Below is the code block which I use to fetch the data from external API.
componentWillReceiveProps({search}){
console.log(search);
}
componentDidMount() {
this.fetchdata('story');
}
fetchdata(type = '', search_tag = ''){
var url = 'https://hn.algolia.com/api/v1/search?tags=';
fetch(`${url}${type}&query=${search_tag}`)
.then(res => res.json())
.then(data => {
this.props.getData(data.hits);
});
}
I'm making the API call in componentDidMount() lifecycle method(as it should be) and getting the data correctly on startup.
But here I need to pass a search value through searchbar component to menubar component to do a custom search. As I'm using only react (not using redux atm) I'm passing it as a prop to the menubar component.
As the mentioned codeblock if I search react and passed it through props, it logs react once (as I'm calling it on componentWillReceiveProps()). But if I run fetchData method inside componentWillReceiveProps with search parameter I receive it goes an infinite loop. And it goes an infinite loop even before I pass the search value as a prop.
So here, how can I call fetchdata() method with updating props ?
I've already read this stackoverflow answers but making an API call in componentWillReceiveProps doesn't work.
So where should I call the fetchdata() in my case ? Is this because of asynchronous ?
Update : codepen for the project
You can do it by
componentWillReceiveProps({search}){
if (search !== this.props.search) {
this.fetchdata(search);
}
}
but I think the right way would be to do it in componentDidUpdate as react docs say
This is also a good place to do network requests as long as you compare the current props to previous props (e.g. a network request may not be necessary if the props have not changed).
componentDidMount() {
this.fetchdata('story');
}
componentDidUpdate(prevProps) {
if (this.props.search !== prevProps.search) {
this.fetchdata(this.props.search);
}
}
Why not just do this by composition and handle the data fetching in the main HoC (higher order component).
For example:
class SearchBar extends React.Component {
handleInput(event) {
const searchValue = event.target.value;
this.props.onChange(searchValue);
}
render() {
return <input type="text" onChange={this.handleInput} />;
}
}
class Main extends React.Component {
constructor() {
this.state = {
hits: []
};
}
componentDidMount() {
this.fetchdata('story');
}
fetchdata(type = '', search_tag = '') {
var url = 'https://hn.algolia.com/api/v1/search?tags=';
fetch(`${url}${type}&query=${search_tag}`)
.then(res => res.json())
.then(data => {
this.setState({ hits: data.hits });
});
}
render() {
return (
<div>
<MenuBar />
<SearchBar onChange={this.fetchdata} />
<Articles data={this.state.hits} />
</div>
);
}
}
Have the fetchdata function in the main component and pass it to the SearchBar component as a onChange function which will be called when the search bar input will change (or a search button get pressed).
What do you think?
Could it be that inside this.props.getData() you change a state value, which is ultimately passed on as a prop? This would then cause the componentWillReceiveProps function to be re-called.
You can probably overcome this issue by checking if the search prop has changed in componentWillReceiveProps:
componentWillReceiveProps ({search}) {
if (search !== this.props.search) {
this.fetchdata(search);
}
}

onclick prepare html when ajax response arrivers and render in react?

Hey I'm new is react my requirement is that when a user clicks on a button an ajax get request get fired to the
server and based of receieved response I have to prepare the html and display it.
below is my code it is not working .. it can be solved in jquery by using async: false but i don't have to use that
any idea how to solve using axios
import React from "react";
import axios from "axios"
class UserItems extends React.Component {
constructor(props) {
super(props);
this.state = {
useritem: ''
}
}
prepareHtmlOnAjaxResponse(){
var user_item = this.state.useritem
// html prepation done here
return preparedHtml;
}
getDatFromServeronclick() {
// getting user data from server is done in this function
// when data is receieved it is stored in a state
var self = this;
var promise = axios.get("http://localhost:4000/user/1/items.json")
promise.then(function (response) {
self.setState({ useritem: response.data.items })
self.prepareHtmlOnAjaxResponse() // prepare html
})
console.log("executing first and returning null")
}
render() {
var result = this.getDatFromServeronclick() // getting undefined value this has to be called onclick
return (
<div>
{result} / result is undefined
</div>
);
}
}
export default UserItems;
You have to use self.setState function instead of self.state assignment, otherwise React wouldn't trigger rerender of the component.
var promise = axios.get("http://localhost:4000/user/1/items.json")
promise.then(function (response) {
self.setState({ useritems: response.data.items })
})
From React's documentation:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
Then in your render function
<button onClick={() => this.getDatFromServeronclick() }> {this.state.useritems.map(user => user.title)} </button>
you can replace user.title with whatever keys your object useritems has.

Categories