Call function on React template render - javascript

I need the template questionsCollected to call the getQuestions function when the template is rendered. I am able to call a function when an event is fired, but in this case I want the function to fire when the template is rendered and populate the options in the select menu.
The ajax call is successful and returns the option items. I can log the return to the console.
The shell of the template is successfully rendering to the page.
How do I call a function from within the template without utilizing an event (onClick, etc)?
Thanks!
class myClass extends React.Component {
constructor () {
super()
this.state = {
isActive: false,
}
this.getQuestions = this.getQuestions .bind(this)
}
getQuestions () {
const token = `xxxx`
const url = 'https://api.com' + token
Ajax.get(url).then(function (response) {
const data = JSON.parse(response.response)
const questions = []
Object.keys(data).forEach(function (value, key) {
questions.push(<option> + data.features[key].properties.question + </option>)
})
return questions
})
}
render () {
return <menuItems
children={this.renderChildren()}
/>
}
renderChildren () {
const questionsCollected = (
<div key='questionText' id='question'>
<select>
<option>Questions</option>
{this.getQuestions}
</select>
</div>
)
return [questionsCollected]
}
export default myContainer

Are you familiar with the react lifecycle? Check out this page, I refer to it often:
http://busypeoples.github.io/post/react-component-lifecycle/
I think you want to move the ajax call to componentDidMount. Upon its success you can call setState to set your questions from the ajax call in state, then your render method reads from the state object for the questions. Kind of like this:
https://daveceddia.com/ajax-requests-in-react/
Hope that helps

Related

React setState not updating state inside stomp.js callback function

I have a page that connects to a SockJS socket over stomp. But when pass a function that is supposed to change the state to the .subscribe callback it doesn't change the page's state (the rest of the function completes normally)
Here is the code:
export default function HomePage() {
...some other states...
const [isIncomingCall, setIsIncomingCall] = useState(false)
function initSocket() {
const socket = new SockJS(`${process.env.API_URL}/ws`);
const stompClient = over(socket);
stompClient.connect(
{},
() => onSocketConnected(stompClient),
onSocketError
)
appContext.setSocket(stompClient)
}
function handleIncomingCall(data) {
const state = JSON.parse(data.body.toLowerCase());
setIsIncomingCall(state)
}
function onSocketConnected(stompClient) {
const options = {
accessToken: cookies['logged-in'],
};
stompClient.send("/app/connect", {}, JSON.stringify(options));
stompClient.subscribe(`/user/search/complete`, handleSearchComplete)
stompClient.subscribe(`/user/search/failed`, handleSearchError)
stompClient.subscribe(`/user/call/incoming`, handleIncomingCall)
}
useEffect(() => {
if (!appContext.socket && cookies['logged-in']) {
initSocket()
}
}, [])
return (
<>
<AnimatePresence>
{
isIncomingCall && <CallModal
onAccept={acceptIncomingCall}
onReject={rejectIncomingCall}
/>
}
</AnimatePresence>
...other page code...
</>
)
}
The initSocket function is called on page render inside the useEffect.
I have tried wrapping the callback with a useCallback and binding it to the page and calling setIsIncomingCall inside an arrow function, but it didn't seem to help.
Ok, so the problem was:
Callbacks were initialized on first visiting the HomePage, but for user to set that they can receive calls, they needed to go to the ProfilePage and flip the switcher. In the process of going back and forth between the pages React loses the pointer to the original isIncomingCall state and sets it on the previous version of the HomePage it seems.

react render called before componentdidmount

In my componentDidMount() I am making an API call "invokeservice" to fetch some data, this call then sets a state object that I use in my render.
The problem I am having is that the invokeservice is not being called and set the first time that the page is loaded, however when I refresh the page the invokeservice returns the data fine and the rest of the script will run. It's only the first time that it returns an null value.
Is it possible to call this invoke service in render function and keep the data ready and display it when user tries to load this page.
TRIED:
1.read that ComponentDidMount is called only after 1st initial render.
2. {this.invokeservice()} i tried to call in render function before return which did initial render but after 5sec it was blank and then again 5sec later it is filled again with values.
render function
public render() {
return (
<div className="monitor">
<div className="monitor__table">
{this.monitorrow(translate("cpu"), this.state.cpuString)}
{this.monitorrow(translate("memory"), this.state.pmemoryString)}
</div>
</div>
);
}
constructor
constructor(props) {
super(props);
this.state = {
cpu: 0,
cpuString: ""
};
setInterval(() => this.invokeservice(), 5000);
}
componentdidMount
public componentDidMount() {
this.invokeservice();
}
invokeservice
private invokeservice() {
var client = new HttpClient();
var url = this.props.baseUrl + '//getMonitorUsage' + '?NC=' + Date.now().toString();
client.get(url, (response) => {
this.setState({
cpu: JSONResponse.GetSystemStateResult.CPUusage
});
}
}
});
}
function
monitorrow(left,right) {
return (
<div className="table__row">
<div className="table__cell__left">
<Text>{left}</Text>
</div>
{ right &&
(<div className="table__cell__right">
<Text>{right}</Text>
</div>)
}
</div>
);
}
It is expected.
From the react docs:
These methods are called in the following order when an instance of a component is being created and inserted into the DOM:
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
Ref: https://reactjs.org/docs/react-component.html
There is componentWillMount which will be called before render(). But is it not advised to be used. Read more on the official docs https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

Exposing the state of a react widget

I have made a react UI widget thats let's the user select a number of different times and dates. The user's current selection is stored in the state of a top level component, DateTimePicker. I then have a widget wrapper like so:
import ...
export default {
new: (args) => {
const store = {
reactElement: <DateTimePicker
startDate={args.startDate}
endDate={args.endDate}
/>
};
return {
getState: () => {
return store.reactElement.getState(); // DOESN'T WORK
},
render: (selector) => {
ReactDOM.render(store.reactElement, document.querySelector(selector));
}
};
}
};
I want to add a validation to make sure that at least X days/times are selected, but this validation needs to be implemented outside of the widget.
For this, I'll need someway of asking the widget of it 's state. i.e. what has the user selected? Although it seems like the state of the class is not part of the public api of a react component.
How can I acess the state, or is there another way I'm missing?
The solution to doing things imperatively from the parent to the child usually involves getting a ref to the child component. Something along these lines:
export default {
new: (args) => {
let myRef = React.createRef();
const store = {
reactElement: <DateTimePicker
ref={myRef}
startDate={args.startDate}
endDate={args.endDate}
/>
};
return {
getState: () => {
return myRef.current.getState();
},
render: (selector) => {
ReactDOM.render(store.reactElement, document.querySelector(selector));
}
};
}
};
With ref={myRef} added as a prop, whenever DateTimePicker gets mounted, it will assign a reference to the mounted component to myRef.current. You can then use that reference to interact directly with the most recently mounted component.

.map() working outside of render function, but fails when used inside render

I'm not a very experienced developer, so I may be missing something simple. I have an array of objects that I am getting from my redux state (this.props.assignments.assignments). I am getting a .map() is not a function when trying to call displayAssignments() in my render return. However, I have another function that is attached to an onClick that is doing the same map and logging "name" into the console, and that one works as expected (when i comment out displayAssignments()). I have no idea why the .map() would work on my onClick but not on displayAssignments.
I included the showAssignments function simply to test if my data could even be mapped...which confused me even more because that one works...
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getAssignments } from '../../Actions/assignmentActions.js';
class Assignments extends Component {
componentDidMount() {
this.props.getAssignments();
};
showAssignments = () => {
console.log(this.props.assignments.assignments);
this.props.assignments.assignments.map(assignment => (
console.log(assignment.name)
));
};
displayAssignments = () => {
this.props.assignments.assignments.map(assignment => {
return (
<div>
<p>{assignment.name}</p>
<p>{assignment.description}</p>
</div>
)
})
};
render() {
return (
<div>
<h1>Assignments</h1>
<button onClick={this.showAssignments}>Click Me</button>
{this.displayAssignments()}
</div>
);
};
};
Assignments.propTypes = {
getAssignments: PropTypes.func.isRequired,
assignments: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
assignments: state.assignments
});
export default connect(mapStateToProps, { getAssignments })(Assignments);
Expected: to render name and description of assignments by way of mapping
Actual: getting this.props.assignments.assignments.map is not a function
You should return your map() as a value of displayAssigments:
displayAssignments = () => {
return this.props.assignments.assignments.map(assignment => {
return (
<div>
<p>{assignment.name}</p>
<p>{assignment.description}</p>
</div>
)
})
};
Or just remove the curly bracket which wrap .map():
displayAssignments = () => this.props.assignments.assignments.map(assignment =>
<div>
<p>{assignment.name}</p>
<p>{assignment.description}</p>
</div>
)
For your error is not function, make sure that this.props.assignments.assignments is an Array.
Your problem is that, given the fact that you call getAssignments() in the componentDidMount method, it means there is a section of time between when the component is mounted and the assignment field is populated in the store in which there are no assignments, hence why you get the .map() is not a function error.
The click handler is only called after the asynchronous operation finishes so assignments is defined by then.
To fix this, you can change your default state for the assignments field to be:
{
"assignments":[]
}
Another problem with your code, as #radonirina-maminiaina has mentioned, is that your displayAssignments function does not return the result of the maps so the HTML will be blank
The Array.map function creates a new array from the given array with the function provided applied to each element.
Another thing to consider is why you need assignments.assignments to get to the list of assignments. You could probably change your actions or reducer to remove the nesting.

How do I create an array after Axios response and Display it in React js

I am having trouble creating an array of titles from an Axios response. The method getTitles(props) receives data from the Axios response. How do I create an array of titles dynamically?
The functions I have tried in Javascript are for loops and EC6 mapping, nothing seems to work. Being new to react I could be missing something but I am not sure what it is.
React code
export default class Featured extends React.Component {
constructor() {
super();
this.state = {
data: null,
}
}
/**
* Received request from server
*/
componentDidMount(){
ApiCalls.articleData()
.then(function(data){
this.setState(function(){
return {
data: data
}
})
}.bind(this));
}
getTitles(props){
//-- What code do I place here?
console.log(props.data)
return ['test title', 'test title 2'];
}
/**
* Render request
*/
render() {
let dataResponse = JSON.stringify(this.state.data, null, 2);
const Articles = this.getTitles(this.state).map((title, i) => <Article key={i} title={title}/> );
return (
<div class="row">{Articles}
<pre>{dataResponse}</pre>
</div>
);
}
}
Axios Code
var ApiCalls = {
articleData: function(id){
return axios.all([getArticles(id)])
.then(function(arr){
return arr[0].data.data;
})
.catch(function (error) {
console.log(error);
})
},
React setState behaves asynchronously . Your articles get rendered before the ajax was called and was not re rendered due to asynchrony in setState.
This is what doc(https://reactjs.org/docs/react-component.html#setstate) says
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.
You can render the article after successful ajax call like below
componentDidMount(){
ApiCalls.articleData()
.then(function(data){
render(<Article data={data}/>, document.getElementById('...'));
}.bind(this));
}
Because of the post above, I was able to fix the issue by the code example below To see a working example goto the git repo
ApiCalls.articleData()
.then(function(data){
const newData = data.map(c => {
return c.attributes.title;
})
const addElement = newData.map((title, i) => <ContentTiles key={i} title={title}/> );
const newState = Object.assign({}, this.state, {
newData: addElement
});
this.setState(newState);
}.bind(this));

Categories

Resources