Update state when view loads - javascript

First time working with React, I need to know how to update the state when the view loads.
All I am trying to do is a GET request in order to get a list of dealers for a Casino Game. Basically, I am missing 1 or 2 steps which are for render the dealers's list in the DOM
I will show what I am doing with my code and after that I will explain what I want
here is the first step in the actions part
getDealerActions.js
class GetDealersActions {
constructor () {
this.generateActions('dealerDataSuccess', 'dealerDataFail');
}
getDealers (data) {
const that = this;
that.dispatch();
axios.get('someroute/get-dealers/get-dealers')
.then(function success (response) {
that.actions.dealerDataSuccess({...response.data});
})
}
};
then we move to the stores
getDealersStore.js
class GetDealersStore {
constructor () {
this.state = {
dealerData : null,
};
}
#bind(GetDealersActions.dealerDataSuccess)
dealerDataSuccess (data) {
this.setState({
dealerData : data,
});
console.log(this.state.dealerData);
}
}
in this case that console.log(this.state.dealerData); returns something like this which is exactly what I need
Object {dealersData: Array[3]}
the problems comes in the component part, honestly because I don't know how to handle the data there
class Dealers extends Component {
constructor (props) {
super(props);
}
static getStores () {
return [ GetDealersStore ];
}
static getPropsFromStores () {
return GetDealersStore.getState();
}
render () {
// here need to implement the .map function but
// as this.props.dealerData is null, I am unable to render it.
return (
<div>
<ActionButton onClick={this._getDealers}>Test</ActionButton>
//in some part here I need to render the list of dealers
</div>
);
}
_getDealers = () => {
console.log(this.props.dealerData);
GetDealersActions.getDealers();
}
}
this console.log(this.props.dealerData); returns null the first time, I have to click twice on _getDealers() in order to get this
{params: Object, query: Object, dealerData: Object}
so, what should I do in the render method in order to get dealerData filled out? and what should I do to update the state once I am in the view ?
If you got it, all I need is to get this.props.dealerData with data and not null. So I can render it in this view.

The way this should be structured is:
class Dealers extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
GetDealersActions.getDealers();
}
render() {
let content;
if (this.state.dealerData) {
content = this.state.dealerData.map((datum) => {
return <div>TODO: JSX!</div>;
);
} else {
content = <div>Loading . . .</div>;
}
return <div>{content}</div>;
}
}

Related

this.state.names.map is not a function

This is my code I just need to fetch data from the API and want to map it but I cannot do it I don't know why. I have gone though all the video tutorials but still couldn't find my problem.
class App extends Component {
state = {
names: [],
isLoaded: false,
}
constructor(props) {
super(props);
}
componentDidMount() {
fetch("https://dog.ceo/api/breeds/list/all")
.then(res => res.json())
.then(name => {
this.setState({
names: name,
isLoaded: true,
})
});
}
renderNames() {
if(this.state.names.length > 0) {
return this.state.names.map((breed) =>
<div key={breed.message}>
{breed.message}
{breed.status}
</div>
)
}
else{
return;
render(){
this isloaded is also an issue every time I find a way this states ruins everything
if (!this.state.isLoaded) {
return <div>loading....</div>
}
else{
return (
<div className="App">
<h1>Choose a name of breed</h1>
{this.renderNames()}
</div>
Bind this to renderNames in constructor.
like:
constructor(props) {
super(props);
this.renderNames = this.renderNames.bind(this)
}
OR
Re-write renderNames as a arrow function.
e.g.
renderNames = () => {
//Your code.
}
First things first... Re-arrange your code.. Place your state within constructor.. Then... Console.log your name value from the promise and see if the data is actually present... Then we will know what next to do.

Component not rerendering on state change?

Whenever setState() is called, the component doesn't seem to rerender. As you can see by my comments, the state does in fact change and render seems to be called again, but if I don't add that if statement and simply add a paragraph tag that displays the data it will give me an error. I'm sure I'm missing something simple, but any help is appreciated.
import React from "react";
import axios from "axios";
import { constants } from "../constants/constants";
const { baseURL, apiKey, userName } = constants;
class User extends React.Component {
constructor(props) {
super(props);
this.state = {
user: []
};
}
componentDidMount() {
let getUserInfo = axios.create({
baseURL,
url: `?
method=user.getinfo&user=${userName}&api_key=${apiKey}&format=json`
});
getUserInfo().then(response => {
let data = response.data;
console.log(data.user.playcount); //logs second, displays correct
this.setState(state => ({
user: data
}));
});
}
render() {
console.log(this.state); //logs first and third, doesn't work on first but does on third
let toReturn;
if (this.state.user.length > 0) {
toReturn = <p>{this.state.user.user.playcount}</p>;
} else {
toReturn = <p>didn't work</p>;
}
return <div>{toReturn}</div>;
}
}
export default User;
React LifeCycle function sequence is Constructor and then it calls render method.
In constructor method it initialises the state which is currently empty user array.
Now it calls render() method as this.state.user is an empty array, referencing something out of it gives an error
this.state.user.user.playcount
this will generate an error if you dont have if condition.
After the first render it will call componentDidMount, now you fetch something update state. As setState occurred, render will be called again Now you have something in this.state.user then displaying will happen.
this.state.user.length > 0 is true
Look at this: https://reactjs.org/docs/react-component.html and https://reactjs.org/docs/conditional-rendering.html
You can right in single tag using conditional render like this
<p>{this.state.user.length ? this.state.user.user.playcount : 'loading'}
Hope this helps.
I think your problem might have something to do with the changing shape of the user value. You initialise the value to an empty array, but then—after the fetch is done—you assume it's an object (by using user.user).
Maybe you could simplify the code a bit to look more like the one below?
/* imports */
class User extends React.Component {
constructor(props) {
super(props);
this.state = {
user: null // Make it explicit there's no value at the beginning.
};
}
componentDidMount() {
let getUserInfo = axios.create(/* ... */);
getUserInfo().then(response => {
let data = response.data;
this.setState({ // No need to for a setter function as you dno't rely on the previous state's value.
user: data.user // Assign the user object as the new value.
});
});
}
render() {
let toReturn;
// Since it's now a `null`, you can use a simple existence check.
if (this.state.user) {
// User is now an object, so you can safely refer to its properties.
toReturn = <p>{this.state.user.playcount}</p>;
} else {
toReturn = <p>No data yet.</p>;
}
return <div>{toReturn}</div>;
}
}
export default User;

Updating React/Redux State from Window function

I have a scenario where I'm trying to update a React/Redux state from a function that's placed on the Window. The function on the window is unable to access the function that's in the React component. Any idea how to bind that function in this kind of setup? This snippet just has a console log where the Redux call would go.
class MyComponent extends Component {
updateRedux = a => {
console.log(a)
}
componentDidMount() {
window.windowFunction = function(a) {
this.updateRedux(a)
}
}
render() {
return (
<Stuff />
)
}
}
this is not accessible inside your function, you need to bind it.
Try with:
class MyComponent extends Component {
updateRedux = a => {
console.log(a)
}
componentDidMount() {
window.windowFunction = function(a) {
this.updateRedux(a)
}.bind(this)
}
render() {
return (
<Stuff />
)
}
}
if you meant that you want to update Redux state with some action (this is the only way to update Redux state by design), then you need to make this action and its functions available to your Component with connect(mapStateToProps, mapDispatchToProps)(Component)
One of the comments above about converting the windowFunction to an arrow function resolved the issue. Thanks!
class MyComponent extends Component {
updateRedux = a => {
console.log(a)
}
componentDidMount() {
window.windowFunction = a => {
this.updateRedux(a)
}.bind(this)
}
render() {
return (
<Stuff />
)
}
}
What you could do is separate the concerns using a presenter and a connected
component, using react-redux. I am assuming you know of this library, comment
if you need more details.
// Simple "presenter", the getComponentData is used to get the data for the
// redux store.
class MyComponentPresenter extends Component {
// returns data for redux
getComponentData () {}
componentDidMount() {
this.props.updateRedux(this); // update Redux
}
render() {
return (
<Stuff />
)
}
}
// This component has the exact same interface, but comes with a updateRedux
// props which automatically dispatches an action
export const MyComponent = connect(null, {
updateRedux(componentInstance) {
return {
type: "updateRedux"
};
}
});
// in the reducer
//
function reducer (state, action) {
switch (action.type) {
case "updateRedux":
return ...
}
}
No more need for globally available function (which in your example is redefined for each instance of MyComponents which is probably not what you want).

what to do with response object in react js

i'm working with react to complete the front end of a rest application.
I have json being sent to the front end, and I use fetch .
fetch('/task')
.then(function(data) {
return data.json();
})
.then(function(json) {
json.tasks.forEach(function(task) {
console.log(task.name)
})
});
So i'm able to console.log each task.name, but where to now? How do I get my component to display each task as a ?
Basically, where in a component does this type of logic go? Do i save the fetch request to a variable and then setState = variable?
this is my component:
class Task extends React.Component {
render() {
return <p> hey </p>
}
}
You need to initialize a state object, which you can update when the fetch is complete:
class Task extends React.Component {
constructor () {
super()
this.state {
tasks: null
}
}
componentDidMount () {
fetch('/task')
.then((data) => {
return data.json()
})
.then((json) => {
this.setState({ tasks: json.tasks })
})
}
renderTaskList () {
if (this.state.tasks) {
return (
<ul>
{this.state.tasks.map((task, i) => <li key={i}>{task.name}</li>)}
</ul>
)
}
return <p>Loading tasks...</p>
}
render () {
return (
<div>
<h1>Tasks</h1>
{this.renderTaskList()}
</div>
)
}
}
Edit: Re-reading this answer, I just wanted to note that it is not necessary to initialize the tasks property of the state object in this case. You could also just do something like:
this.state = {}
However, I think there is some value in explicitly naming the various properties of your state object, even if they are initialized as null. This allows you to write components whose state is documented in the constructor, and will prevent you or your teammates from later guessing how a component's state is modeled.

Reactjs: How to fetch data to loaded before the component is mounted?

Something weird is happening, I've been reading the React docs and they talk about the lifecycle and how you can do somestuff before your component is rendered. I am trying, but everything I try is failing, always the component makes the render first and after calls componenWillMount, ..didMount, etc.. and after the call of those functions, the render happens again.
I need to load the data first in order to fill the state because I don't want initial state to be null, I want it with data since the initial rendering.
I am using Flux and Alt, here is the
action
#createActions(flux)
class GetDealersActions {
constructor () {
this.generateActions('dealerDataSuccess', 'dealerDataFail');
}
getDealers (data) {
const that = this;
that.dispatch();
axios.get(`${API_ENDPOINT}/get-dealers/get-dealers`)
.then(function success (response) {
console.log('success GetDealersActions');
that.actions.dealerDataSuccess({...response.data});
})
}
}
then the store
#createStore(flux)
class GetDealersStore {
constructor () {
this.state = {
dealerData : null,
};
}
#bind(GetDealersActions.dealerDataSuccess)
dealerDataSuccess (data) {
this.setState({
dealerData : data,
});
}
}
and the component
#connectToStores
export default class Dealers extends Component {
static propTypes = {
title : React.PropTypes.func,
}
static contextTypes = {
router : React.PropTypes.func,
}
constructor (props) {
super(props);
this.state = {
modal : false,
dealerData : this.props.dealerData,
}
}
componentWillMount () {
GetDealersActions.getDealers();
this.setState({
dealerData : this.props.dealerData.dealersData,
})
}
static getStores () {
return [ GetDealersStore ];
}
static getPropsFromStores () {
return {
...GetDealersStore.getState(),
}
}
render () {
return (<div>
<div style={Styles.mainCont}>
{!!this.props.dealerData ?
this.props.dealerData.dealersData.map((dealer) => {
return (<div>HERE I AM RENDERING WHAT I NEED</div>);
}) : <p>Loading . . .</p>
}
</div>
</div>
);
}
}
as you can see in the component part I have this
constructor (props) {
super(props);
this.state = {
modal : false,
dealerData : this.props.dealerData,
}
}
componentWillMount () {
GetDealersActions.getDealers();
this.setState({
dealerData : this.props.dealerData.dealersData,
})
}
which is telling me that dealerData is undefined or can not read property of null.
All I need is to know a technique where I can fetch the data before the initial renders occurs. So I can filled out the state and the start working with that data.
React does guarantee that state assignments in componentWillMount will take place before the first render. As you well stated in the comments:
Invoked once, both on the client and server, immediately before the initial rendering occurs. If you call setState within this method, render() will see the updated state and will be executed only once despite the state change.
However, the asynchronous actions requested there will not immediately update your store. Calling GetDealersActions.getDealers(); will issue that the store is updated with new content, but the response will only arrive later in the event queue. This means that this.props.dealersData does not change during the function and setState will attempt to read property "dealersData" of an undefined property. Regardless, the requested content cannot be visible at the first render.
My advice is the same as the one in a similar question. Preventing the component from rendering that content until it becomes available, as you did, is an appropriate thing to do in a program. Alternatively, render a loader while your "dealersData" hasn't arrived.
For solving your particular problem, remove that setState from componentWillMount. All should work well if your parent component is properly listening for changes and propagating them to the children's props.
componentWillMount () {
GetDealersActions.getDealers();
}
The best answer I use to receive data from server and display it
constructor(props){
super(props);
this.state = {
items2 : [{}],
isLoading: true
}
}
componentWillMount (){
axios({
method: 'get',
responseType: 'json',
url: '....',
})
.then(response => {
self.setState({
items2: response ,
isLoading: false
});
console.log("Asmaa Almadhoun *** : " + self.state.items2);
})
.catch(error => {
console.log("Error *** : " + error);
});
})}
render() {
return(
{ this.state.isLoading &&
<i className="fa fa-spinner fa-spin"></i>
}
{ !this.state.isLoading &&
//external component passing Server data to its classes
<TestDynamic items={this.state.items2}/>
}
) }

Categories