Use data outside fetch from API ( React ) - javascript

I receive an array of objects from the API fetch, but i can't pass it on < ResponseTable data={} /> if i pass the car, it works.
import React from 'react'
import ResponseTable from './responsetable'
var car = [{type:"Fiat", model:"500", color:"white"}];
class Table extends React.Component{
constructor(props){
super(props);
this.state = {};
}
fetchData() {
return fetch('http://localhost:8000/sprints/23')
.then(function(response) {
console.log(response.json);
})
.then(function(myJson) {
return myJson;
});
}
componentDidMount(){
this.fetchData();
}
render(){
return(
<div>
<ResponseTable data={} />
</div>
);
}
}
export default Table;
Any help is welcome!

Set the response in state. car works because it comes from global scope.
class Table extends React.Component{
constructor(props){
super(props);
this.state = {
data: {}
}
}
fetchData() {
return fetch('http://localhost:8000/sprints/23')
.then(function(response) {
console.log(response.json);
})
.then((myJson) => {
this.setState({data: myJson});
});
}
componentDidMount(){
this.fetchData();
}
render(){
return(
<div>
<ResponseTable data={this.state.data} />
</div>
);
}
}

When you resolve your fetch, you will want to set your state component and then pass that state to your ResponseTable data
class Table extends React.Component{
constructor(props){
super(props);
this.state = {
myJson: null // define as null
}
}
fetchData() {
return fetch('http://localhost:8000/sprints/23')
.then((response) => {
console.log(response.json);
})
.then((myJson) => {
this.setState({myJson: myJson})
});
}
componentDidMount(){
this.fetchData();
}
render(){
return(
<div>
<ResponseTable data={this.state.myJson} />
</div>
);
}
}
export default Table;
Note we set the myJson state as null.
We then fetch the data. I have changed the .then functions to arrow functions so that this is scoped to the component.
We then pass this.state.myJson as a property to your child component

Why not throw the response into a state object to pass as a prop?
import React from 'react'
import ResponseTable from './responsetable'
var car = [{type:"Fiat", model:"500", color:"white"}];
class Table extends React.Component{
constructor(props){
super(props);
this.state = {
data: {}
}
}
fetchData() {
return fetch('http://localhost:8000/sprints/23')
.then(function(response) {
this.setState({data: response.json})
console.log(response.json);
})
}
componentDidMount(){
this.fetchData();
}
render(){
return(
<div>
<ResponseTable data={this.state.data} />
</div>
);
}
}
export default Table;

Related

How to wait state change to render?

So, I need to get the response from a request and then send it to another component. The problem is that my request isn't finished when the component call happens. So what I end up getting on the "TableComponent" is an empty array
This is the component I'm making the request at:
class Carrinho extends Component {
constructor(props) {
super(props);
this.getMateriais()
}
async getMateriais() {
let service = new MateriaisService();
service.getMateriais().then(res => res.json()).then((result) => {
this.setState({materiais: result})
})
}
render() {
return (
<div>
<TableComponent materiais={this.state.materiais} itens={this.state.array_teste}></TableComponent>
</div>
);
}
And this is how I'm setting my state on TableComponent.js :
constructor(props) {
super(props);
this.state = {
materiais : props.materiais,
}
This won't work, because this.getMateriais() call in the constructor, won't trigger a new render. You'll need to use componentDidMount life cycle and async/await syntax.
class Carrinho extends Component {
constructor(props) {
super(props);
this.getMateriais()
}
async componentDidMount(){
await this.getMateriais();
}
async getMateriais() {
let service = new MateriaisService();
const result = await service.getMateriais();
const data = await result.json();
this.setState({ materiais: result });
}
render() {
return (
<div>
<TableComponent materiais={this.state.materiais} itens={this.state.array_teste}></TableComponent>
</div>
);
}
However, async/await is not recommendable to deal with promises in React programming model. Instead, you should render a different component or a loading, while waiting.
class Carrinho extends Component {
constructor(props) {
super(props);
}
componentDidMount(){
this.getMateriais();
}
getMateriais() {
let service = new MateriaisService();
service.getMateriais().then(res => res.json()).then((result) => {
this.setState({materiais: result})
})
}
render() {
return (
<div>
{this.state.materiais && <TableComponent materiais={this.state.materiais} itens={this.state.array_teste}></TableComponent>}
</div>
);
}

Pass this.state variable through App Context

When I set the array data using the function getData() then try to call it in the function updateData() I get an error saying the this.state.data is undefined. Any thoughts on how I can pass a this.state variable from one function to another function in the app context provider?
Example code is below:
Any thoughts? Thank you!
export class AppProvider extends React.Component {
constructor(props) {
super(props);
(this.state = {
data: [],
});
}
getData = async () => {
const data = "abc"
this.setState({
data,
});
}
updateData = async () => {
console.log(this.state.data)
}
render() {
return (
<AppContext.Provider value={this.state}>
{this.props.children}
</AppContext.Provider>
);
}
}
Three Things i would like to say,
you want to add the state variables separately so you want to do value={{data:this.state.data}}
if you plan on using these functions in another component you want to add these functions to the value prop as well
remove the async from the functions since there is no Promise to be resolved
export class AppProvider extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
getData = () => {
const data = "abc";
this.setState({
data
});
};
updateData = () => {
console.log(this.state.data);
};
render() {
return (
<AppContext.Provider
value={{
data: this.state.data,
getData: this.getData,
updateData: this.updateData
}}
>
{this.props.children}
</AppContext.Provider>
);
}
}
checked this in a small example, CodeSandbox here

Local JSON file is not parsing in React

I have a large JSON file which has around 5000 entries and when I parse it using fetch(), it doesn't show up in browser.
Here's my code:
import React from 'react';
import './Box.css';
class Box extends React.Component {
constructor() {
super()
this.state = {movieName: []}
}
componentDidMount() {
fetch('./MovieDatabaseShort.json')
.then(a => a.json())
.then(movieName => this.setState({movieName}));
}
renderMovies() {
const { movieName } = this.state;
return movieName.map(a => {
<h1 key={ a.id } className='heading'>{a.title}</h1>;
});
}
render() {
return <div className="box">{this.renderMovies()}</div>;
}
}
export default Box;
I just want to put all the movies titles.
import React from 'react';
import './Box.css';
class Box extends React.Component {
constructor() {
super()
this.state = {movieName: []}
}
componentDidMount() {
fetch('https://support.oneskyapp.com/hc/en-us/article_attachments/202761627/example_1.json')
.then(a => a.json())
.then(movieName => this.setState({movieName: movieName.color}));
}
render() {
console.log( this.state );
return <div className="box">{this.state.movieName}</div>;
}
}
export default Box;
EDIT- In second code, I just copied random json file from net and it works fine. I think its's due to size of the json file I have. It's 250k+ lines.
Update- This works. I think problem is due to fetch()
import React from 'react';
import './Box.css';
import a from './MovieDatabaseShort.json'
class Box extends React.Component {
constructor() {
super()
this.state = {movieName: []}
}
componentDidMount() {
this.setState({movieName: a});
}
renderBox() {
const { movieName } = this.state;
return movieName.map(k => {
return <h1 className='heading'>{k.title}</h1>;
})
}
render() {
return (
<div className='box'>{this.renderBox()}</div>
);
}
}
export default Box;`
First of all, there are some places you should change in your code.
You should keep an array property in your state for all movies: movies: []
You should map this state value, then render some JSX.
Use componentDidMount instead of componentWillMount since it will be deprecated in a future release.
Here is the example code:
class Box extends React.Component {
constructor() {
super();
this.state = { movies: [] };
}
componentDidMount() {
fetch("./MovieDatabaseShort.json")
.then(res => res.json())
.then(movies => this.setState({ movies }));
}
renderMovies() {
const { movies } = this.state;
return movies.map(movie => (
<h1 key={movie.title} className="heading">
{movie.title}
</h1>
));
}
render() {
return <div className="box">{this.renderMovies()}</div>;
}
}
If you still don't see anything maybe fetch would the problem here. Then, try this:
class Box extends React.Component {
constructor() {
super();
this.state = { movies: [] };
}
componentDidMount() {
import("./MovieDatabaseShort.json").then(movies =>
this.setState({ movies })
);
}
renderMovies() {
const { movies } = this.state;
return movies.map(movie => (
<h1 key={movie.title} className="heading">
{movie.title}
</h1>
));
}
render() {
return <div className="box">{this.renderMovies()}</div>;
}
}
Again, if nothing is shown up please share you JSON file with us as well as check your console if there is any error.
What it looks like you want to do is to save all movies into an array on your state. That would look more like this:
constructor() {
super()
this.state = {movies: []}
}
componentWillMount() {
fetch('./MovieDatabaseShort.json')
.then(a => a.json())
.then(b => this.setState({movies: b}));
}
Then in your render function you would loop over your movies and display the title:
render() {
const { movies } = this.state;
return (
<div className='box'>
{movies.map(movie => <h1 className='heading'>{movie.title}</h1>)}
</div>
);
}
Another way using hook can be the following. In my case I need to take configuration data from a json file
import _data from '../../json/config.json';
export const Mapa = () => {
const [config, setConfig] = useState(null);
useEffect(()=>{
setConfig(_data );
},[]);
}

Getting the ref from a dynamic component when using Redux, React and react-router-dom 4.x

I have the following class
class MatchBox extends React.Component {
constructor(props) {
super(props);
this.countdownHandler = null;
this.showBlocker = true;
this.start = this.start.bind(this);
}
start() {
...
}
render() {
...
return (
<div style={ styles.mainContainer } className="fluid-container">
...
</div>
);
}
};
function mapStateToProps(state) {
...
}
function matchDispatchToProps(dispatch) {
...
}
export default withRouter(connect(mapStateToProps, matchDispatchToProps, null, { withRef: true })(MatchBox));
which is used in this class
class GameBox extends React.Component {
constructor(props) {
super(props);
...
}
render() {
var mainElement = null;
switch(this.props.mainElement.element) {
case 'SEARCHING': mainElement = <SearchingBox gameType={ this.props.gameType }/>; break;
case 'MATCH': mainElement = <MatchBox ref='matchBox'/>; break;
default: mainElement = <SearchingBox/>;
}
return (
<div style={ styles.mainContainer } className="fluid-container">
{ mainElement }
</div>
);
}
};
function mapStateToProps(state) {
...
}
function matchDispatchToProps(dispatch) {
...
}
export default withRouter(connect(mapStateToProps, matchDispatchToProps, null, { withRef: true })(GameBox));
And I can't get the ref of the object MatchBox. I tried with this.refs.matchBox and is null, also tried getting directly from ref(ref={(r) => { // r is null } }) and I don't know what to try anymore.
I'm using react-router-dom 4 and I don't know if function withRouter affect the outcome component.
It's not pretty, but I think this is the solution. withRouter exposes the child ref via a wrappedComponentRef callback, which gets us to the connect hoc. That exposes its child ref via getWrappedInstance if you pass the withRef attribute as you did. So you just have to combine both of those.
class GameBox extends React.Component {
matchboxRefCallback = (connectHOC) => {
this.matchboxRef = connectHOC ? connectHOC.getWrappedInstance() : null;
}
render() {
return <MatchBox wrappedComponentRef={this.matchboxRefCallback}/>;
}
}
Much more cleaner solution would be to create a HOC. which will forward the ref to actual component
const matchBoxHOC = (WrappedComponent) => {
class MatchBoxHOC extends React.Component {
render() {
const { forwardRef, ...rest } = this.props;
return <WrappedComponent {...rest} ref={forwardRef} />;
}
}
const WithRouterMatchBoxHOC = withRouter(MatchBoxHOC, { withRef: true });
return React.forwardRef((props, ref) => {
return <WithRouterMatchBoxHOC {...props} forwardRef={ref} />;
});
}
Call is like
export default matchBoxHOC(connect(mapStateToProps, matchDispatchToProps, null, { withRef: true })(MatchBox));

React - Passing props to 'ES6 extends' components, and the correct use of 'bind'

I am trying to create a simple Inbox, that makes an api call and returns json object containing a list of messages. This is then passed via props down to the 'InboxList' and then 'InboxItem' components. However, I am struggling to get props down to render each item.
I am also receiving an error when using bind(this), which is the following.
index.js:28 Uncaught (in promise) TypeError: (intermediate value).bind is not a function(…)
I believe i need to bind within my componentDidMount method due to es6 syntax, but I do not understand what the error refers to. Fwiw the json data is coming back successfully.
Any leads on this would be most appreciated
export default class Inbox extends Component {
constructor() {
super();
this.state = {
messages: [],
};
}
componentDidMount() {
this.serverRequest = axios.get('/api/messages')
.then(res => {
console.log(res.data);
})
.catch(res => {
if (res instanceof Error) {
console.log(res.message);
} else {
console.log(res.data);
}
this.setState({
messages: res.data,
}.bind(this));
});
}
render() {
return (
<div>
<InboxHeader />
<InboxList messages={this.state.messages} />
</div>
);
}
}
export default class InboxList extends Component {
render() {
return (
<ul className="dm-inbox__list">
{this.props.messages.map(message =>
<InboxItem message={message} />
)}
</ul>
);
}
}
read this for more info http://reactkungfu.com/2015/07/why-and-how-to-bind-methods-in-your-react-component-classes/
A fix for you below. no need to bind to the promise
https://www.toptal.com/javascript/10-most-common-javascript-mistakes
xport default class Inbox extends Component {
constructor() {
super();
this.state = {
messages: [],
};
}
componentDidMount() {
//serverRequest remove it
//this.serverRequest = axios.get('/api/messages')
axios.get('/api/messages')
.then((response)=>{
console.log(response);
if(response.status===200){
return response.data;
} else {
throw new Error("Server response wasn't ok");
}
})
.then((responseData)=>{
this.setState({messages:responseData});
}).catch((error)=>{
});
}
render() {
return (
<div>
<InboxHeader />
//the messages array might be still empty cause the network call is async so do a check in the inbox list
<InboxList messages={this.state.messages} />
</div>
);
}
}
export default class InboxList extends Component {
render() {
//check if null or empty if not yet resolved show loading eg spinner
if(!this.props.messages){
return <div>loading....</div>;
}
return (
<ul className="dm-inbox__list">
{this.props.messages.map(message =>
<InboxItem message={message} />
)}
</ul>
);
}
}
import React, {Component} from 'react';
export const fetchResource = msg => WrappedComponent =>
class extends Component {
constructor(props){
super(props);
this.state = {
resource: null,
msg: null
};
}
componentDidMount(){
this.setState({msg})
axios.get('https://api.github.com/users/miketembos/repos')
.then((response)=>{
console.log(response);
if(response.status===200){
return response.data;
} else {
throw new Error("Server response wasn't ok");
}
})
.then((responseData)=>{
this.setState({resource:responseData});
}).catch((error)=>{
this.props.history.pushState(null, '/error');
});
}
render(){
const {resource} = this.state
return <Posts {...this.props} {...resources } />
}
}

Categories