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
Related
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>
);
}
I want to keep some functions outside of my component for easier testing. However, I cannot change state with these functions because they cannot reference the component's state directly.
So I currently have the hacky solution where I set the function to a variable then call this.setState. Is there a better convention/more efficient way to do this?
Example function code in Tester.js:
const tester = () => {
return 'new data';
}
export default tester;
Example component code in App.js (without imports):
class App extends Component {
constructor() {
super();
this.state = {
data: ''
}
}
componentDidMount(){
let newData = tester();
this.setState({ data: newData })
}
render() {
return(
<div>{this.state.data}</div>
)
}
}
You could bind your tester function like this (this approach doesn't work with arrow functions):
function tester() {
this.setState({ data: 'new Data' });
}
class App extends Component {
constructor() {
super();
this.state = {
data: '',
};
this.tester = tester.bind(this);
}
componentDidMount() {
this.tester();
}
render() {
return (
<div>{this.state.data}</div>
);
}
}
But I would prefer a cleaner approach, where you don't need your function to access this (also works with arrow functions):
function tester(prevState, props) {
return {
...prevState,
data: 'new Data',
};
}
class App extends Component {
constructor() {
super();
this.state = {
data: '',
};
}
componentDidMount() {
this.setState(tester);
}
render() {
return (
<div>{this.state.data}</div>
);
}
}
You can pass a function to setState() that will return a new object representing the new state of your component. So you could do this:
const tester = (previousState, props) => {
return {
...previousState,
data: 'new data',
};
}
class App extends Component {
constructor() {
super();
this.state = {
data: ''
}
}
componentDidMount(){
this.setState(tester)
}
render() {
return(
<div>{this.state.data}</div>
)
}
}
The reason being that you now have access to your component's previous state and props in your tester function.
If you just need access to unchanging static placeholder values inside of your app, for example Lorem Ipsum or something else, then just export your data as a JSON object and use it like that:
// testData.js
export const testData = {
foo: "bar",
baz: 7,
};
...
// In your app.jsx file
import testData from "./testData.js";
const qux = testData.foo; // "bar"
etc.
I have a basic rect component and I already figured out how to get data from a protected rest api, however I am not sure how to render it in the component and how to call that function, or in which lifecycle I should call the function.
import React, { Component } from 'react';
import LayoutContentWrapper from '../components/utility/layoutWrapper';
import LayoutContent from '../components/utility/layoutContent';
var q = require('q');
var Adal = require('../adal-webapi/adal-request');
function getValues() {
var deferred = q.defer();
Adal.adalRequest({
url: 'https://abc.azurewebsites.net/api/values'
}).then(function(data) {
console.log(data);
}, function(err) {
deferred.reject(err);
});
return deferred.promise;
}
export default class extends Component {
render() {
return (
<LayoutContentWrapper style={{ height: '100vh' }}>
<LayoutContent>
<h1>Test Page</h1>
</LayoutContent>
</LayoutContentWrapper>
);
}
}
The lifecycle method you choose to fetch the data in will largely depend on whether or not you need to update the data at any point and re-render, or whether that data depends on any props passed to the component.
Your example looks as though it is a one time API call that doesn't depend on any props, so placing it in the constructor would be valid.
I would move the getValues code to within the class, and do something like this. Note: I've used async/await, but you could use promise callbacks if you prefer.
export default class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
data: []
}
this.fetchData();
}
async fetchData() {
try {
const data = await this.getValues();
!this.isCancelled && this.setState({ data });
} catch(error) {
// Handle accordingly
}
}
getValues() {
// Your API calling code
}
componentWillUnmount() {
this.isCancelled = true;
}
render() {
const { data } = this.state;
return (
<ul>
{data && data.map(item => (
<li>{item.name}</li>
))}
</ul>
);
}
}
If you needed to fetch the data again at any point, you might use one of the other lifecycle hooks to listen for prop changes, and call the fetchData method again.
Note the inclusion of a failsafe for the component un-mounting before the async call has finished, preventing React from throwing an error about setting state in an unmounted component.
something like this...
export default class extends React.Component {
constructor(props) {
super(props);
// initialize myData to prevent render from running map on undefined
this.state = {myData: []};
}
// use componentDidMount lifecycle method to call function
componentDidMount() {
// call your function here, and on promise execute `setState` callback
getValues()
.then(data => {
this.setState({myData: data})
}
}
render() {
// create a list
const items = this.state.myData.map((datum) => {
return <LayoutContent>
<h1>{datum}</h1>
</LayoutContent>
});
// return with the list
return (
<LayoutContentWrapper style={{ height: '100vh' }}>
{items}
</LayoutContentWrapper>
);
}
}
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;
I'd like to execute some async function to fetch some data from db without freezing the UI.
This is the code I wrote
export default class CustomComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
value:0
};
}
componentWillMount() {
this.fetchData().then(result => { this.setState(value:result);});
}
async fetchData() {
var appState = await someMethod()
return appState;
}
someMethod() {
return new Promise(resolve => {
resolve(queryFromDB())
});
}
queryFromDB() {
// Returns a value fetched from Realm
let events = this.realm.objects("Event");
return events.length;
}
render() {
return (
<Text> {this.state.value} </Text>
);
}
}
The problem is that it does execute on the main thread, freezing the app.
What's the error?
Seems like your code has syntax errors. You have written all your code inside the constructor. Try this.
export default class CustomComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
value:0
};
}
componentWillMount() {
this.fetchData().then(result => { this.setState(value:result);});
}
async fetchData() {
var appState = await someMethod()
return appState;
}
someMethod() {
return new Promise(resolve => {
resolve(queryFromDB())
});
}
queryFromDB() {
// Returns a value fetched from Realm
let events = this.realm.objects("Event");
return events.length;
}
render() {
return (
<Text> {this.state.value} </Text>
);
}
}