ReactJs - Function in Render doesn't Show Up - reactjs

**
Update: This questions has an answer that worked. It is important to
note that even though you have a return statement in your function
called within render(), it is still important to wrap the entire loop
in a parent "return" in order for it to render properly on state
change. This is a different common issue where state is not updated
properly.
I have the following ClientList component, which shows a list of customers retrieved from database.
Below in the Render() function, i am calling the showList function which will display a list once this.props.clientList Reducer is populated.
Problem is... if I were to call the showList codes directly inside the Render() method, it will show.
IF i were to put it in a showList function, and call {this.showList} it doesn't shows up in the render.
I have the screen shot of the console as well, showing that the list is populated already.
Is this method disallowed? I see many tutorials teaching us to do this but it's not working for me. What are the restrictions to use this method to return the codes for render?
class ClientList extends Component {
constructor(props) {
super(props);
this.state = {
clientId : ''
}
this.getClientList = this.getClientList.bind(this);
this.showList = this.showList.bind(this);
console.log('initializing', this.props);
}
componentDidMount(){
this.getClientList();
}
getClientList() {
if (this.props.actions) {
this.props.actions.getClientList(); //This is an ajax action to retrieve from Api
}
}
showList() {
//If i put all the codes below directly in Render, it will show.
console.log('props from showList', this.props.clientList);
this.props.clientList && Object.keys(this.props.clientList).reverse().map((index,key) => {
return (
<div key={key}>
<div><a onClick={() => this.showProfileBox(this.props.clientList[index].customerId)}>Name: {this.props.clientList[index].firstname} {this.props.clientList[index].lastname}</a><span className="pull-right"><Link to={"/client/" + this.props.clientList[index].customerId}>Edit</Link></span></div>
</div>
);
})
}
render() {
console.log('rendering', this.props);
return (
<div>
<Col xs={12} md={8}>
<h1>Client List</h1>
{ this.showList() } // <= This function doesn't print
</Col>
</div>
)
}
}
function mapStateToProps(state) {
return {
clientList: state.clientList,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(clientActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(ClientList);

Your should return the value from showList() method. As of now you were returning the value for map method, but not for the entire showList() method. Thats y it is painting nothing in the page
`
showList() {
return (
//removed unnecessary {}
this.props.clientList && Object.keys(this.props.clientList).reverse().map((index,key) => {
return (
<div key={key}>
<div><a onClick={() => this.showProfileBox(this.props.clientList[index].customerId)}>Name: {this.props.clientList[index].firstname} {this.props.clientList[index].lastname}</a><span className="pull-right"><Link to={"/client/" + this.props.clientList[index].customerId}>Edit</Link></span></div>
</div>
);
})
);
}
`

You don't need to bind showList in the constructor.
Remove it and you should be fine.
Also, as #JayabalajiJ pointed out, you need to return something out of showList otherwise you won't see the final result.
class ClientList extends React.Component {
constructor() {
super()
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
console.log('click')
}
showList() {
return <button onClick={this.handleClick}>From showList</button>
}
render() {
return (
<div>
<button onClick={this.handleClick}>Click-me</button>
{this.showList()}
</div>
)
}
}
ReactDOM.render(
<ClientList />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Related

React SetState returns [Object Object] even though api returns valid json

Fetch is successful in below code (I have confirmed by looking at console - Network in browser) but the setState is not updating the allWeights array in state with the array of documents from MongoDB returned by API. When I use console.log() it prints what api returns but just setState is not working. I understand when render() executes allWeights array will have no value however after componentDidMount executes allWeights must have value if setState behaves as expected. I have searched for solutions provided for similar issue but they are not helpful for my issue. As the setState is not working the map function in render() throws error. Kindly help. I am stuck with this for last 3 days.
import React, {Component} from "react";
//
class TeamWeightsMain extends Component {
constructor(props) {
super(props);
this.state = {
allWeights: []
}
}
//
componentDidMount() {
console.log(`Entered componentDidMount()`);
fetch("http://localhost:8080/getallemployees")
.then(response => response.json())
.then((data) => {
this.setState(
{allWeights : data}
);
});
}
//
render() {
console.log(`Entered render() - allWeights value now : ${this.state.allWeights.toString() }`);
if(this.state.allWeights !== undefined && this.state.allWeights.length > 0) {
console.log(`this.state.allWeights.length: ${this.state.allWeights.length}`);
console.log(`this.state.allWeights: ${this.state.allWeights}`);
console.log(`this.state: ${this.state}`);
return(
<main>{
this.state.allWeights.map((emp,i) => {
return <div key={i}>
{emp.empName}
{emp.employeeWeights.map((weight,j) => {
return <div key={j} className="indentWeight">
Date: {new Date(weight.weighedOn).toLocaleDateString()}
{' '}
Weight: {weight.empWeight}
</div>
})}
</div>}
)
}
</main>)
}
else {
console.log(`Inside Not authorized : ${this.state.allWeights}`);
return (
<main>
<h2>Teamweights</h2>
<div className="indentWeights">
Sample text
</div>
</main>
)
}
}
}
//
export default TeamWeightsMain;

ReactJS Assigning Value inside a class component method to props

I'm trying to access the index of the selected tab in a React component so as to map it to props as follows:
class AttendanceDetail extends React.Component {
handleSelect(key, props) {
console.log(key)
props.index = key;
}
render(){
const {single_class, courses, attendances} = this.props;
// console.log(this.state);
if(single_class) {
return(
<div className='container content-section'>
// Some irrelevant Code
<Tabs defaultActiveKey={0} onSelect={this.handleSelect} id="uncontrolled-tab-example">
{ courses.map((course, index) => {
return (
<Tab eventKey={index} title={course.course + " year " + course.yearofstudy}>
//Other irrelevant code...
</Tab>
)
})}
</Tabs>
</div>
)
} else {
return (
<div className='container content-section'>
Loading Unit details...
</div>
);
}
}
}
So basically the handleSelect method is what determines the index of the selected tab and logs it to the console. The problem is, I'm tring to map that key (index) to props so as to access it else where but to no avail. Could someone help me out? What am I missing?
You're not supposed to set the component's props, only read. You can use state within the component:
export class Wrapper extends React.Component {
constructor() {
this.state = {
index: 0 //initial state
}
}
handleSelect(index, props) {
this.setState({index})
}
render() {
return (
<span>{this.state.index}</span>
)
}
}
you can read more on the official docs.
if i understood the scenario correctly, you need to log index value of the currently active tab. try using onFocus event handler to get the index value of the currently visible tab and set the state that will be used by handleSelect
constructor(props){
super(props);
this.state = {
index:''
}
}
the handler definition
setIndex = (index) => {
this.setState({index})
}
update handleSelect
handleSelect(index) {
console.log(index)
// call event handler of parent component eg: this.props.getIndex(index);
}
update tabs component handler
<Tabs defaultActiveKey={0} onSelect={() => {this.handleSelect(this.state.index)}} id="uncontrolled-tab-example">
call handler on focus of tab
<Tab
onFocus={() => {this.setIndex(index)}}
eventKey={index}
title={course.course + " year " + course.yearofstudy}>
//Other irrelevant code...
</Tab>

How to scroll to bottom when props changed in react-virtualized?

I have component App with List from react-virtualized library.
And I need on initial render, that my List scroll to bottom.
And I did it, when added scrollToIndex option. But when I add new object in my list array, it does not scroll to my last added object. How can I fix it? And is it good solution to use "forceUpdate()" function?
import { List } from "react-virtualized";
import loremIpsum from 'lorem-ipsum';
const rowCount = 1000;
const listHeight = 600;
const rowHeight = 50;
const rowWidth = 800;
class App extends Component {
constructor() {
super();
this.renderRow = this.renderRow.bind(this);
this.list = Array(rowCount).fill().map((val, idx) => {
return {
id: idx,
name: 'John Doe',
image: 'http://via.placeholder.com/40',
text: loremIpsum({
count: 1,
units: 'sentences',
sentenceLowerBound: 4,
sentenceUpperBound: 8
})
}
});
}
handle = () => {
this.list = [...this.list, { id: 1001, name: "haha", image: '', text: 'hahahahahaha' }];
this.forceUpdate();
this.refs.List.scrollToRow(this.list.length);
};
renderRow({ index, key, style }) {
console.log('____________', this.list.length);
return (
<div key={key} style={style} className="row" >
<div className="image">
<img src={this.list[index].image} alt="" />
</div>
<div onClick={this.handle}>{this.state.a}</div>
<div className="content">
<div>{this.list[index].name}</div>
<div>{this.list[index].text}</div>
</div>
</div>
);
}
render() {
return (
<div className="App">
<div className="list">
<List
ref='List'
width={rowWidth}
height={listHeight}
rowHeight={rowHeight}
rowRenderer={this.renderRow}
rowCount={this.list.length}
overscanRowCount={3}
scrollToIndex={this.list.length}
/>
</div>
</div>
);
}
}
export default App;
You mentioning you need to scroll to the bottom when the list item is changed and to be honest i don't like to use forceUpdate. As mentioned on the React docs:
Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render().
Luckily, one of React lifecycle method is suitable for this case, it is call componentDidUpdate. But you need to do some refactor of your code. Instead using private field, i suggest to put it on state/props.
This method will invoked immediately after updating props/state occurs. However, This method is not called for the initial render.
What you need to do is, compare the props, is it change or not? Then call this.refs.List.scrollToRow(this.list.length);
Sample code
class App extends Component {
constructor() {
this.state = {
list: [] // put your list data here
}
}
// Check the change of the list, and trigger the scroll
componentDidUpdate(prevProps, prevState) {
const { list } = this.state;
const { list: prevList } = prevState;
if (list.length !== prevList.length) {
this.refs.List.scrollToRow(list.length);
}
}
render() {
// usual business
}
}
more reference for React lifecyle methods:
https://reactjs.org/docs/react-component.html#componentdidupdate

How can I use directly function in React?

In this code, I would like to show data with directly using function _renderMovies
not like
{movies? this._renderMovies(): 'Loading!' }
cuz I don't want to show Loadings
Do you guys have an idea of how can I use directly function _renderMovies?
My code :
import React, { Component } from 'react';
import L_MovieList from './L_MovieList';
import L_Ranking from './L_Ranking';
import './L_BoxOffice.css';
class L_BoxOffice extends Component {
state ={
}
constructor(props) {
super(props);
this._renderMovies = this._renderMovies.bind(this);
}
componentDidMount(){
this._getMovies();
}
_renderMovies=()=>{
const movies= this.state.movies.map((movie)=>{
console.log(movie)
return <L_Ranking
title={movie.title_english}
key={movie.id}
genres={movie.genres}
/>
})
return movies
}
_getMovies = async()=>{
const movies = await this._callApi()
this.setState({
//movies : movies
movies
})
}
_callApi=()=>{
return fetch('https://yts.am/api/v2/list_movies.json?sort_by=download_count')
.then(potato=> potato.json())
.then(json=> json.data.movies)
.catch(err=>console.log(err))
}
render() {
const{movies}=this.state;
return (
<div>
<div className={movies ? "L_BoxOffice" : "L_BoxOffice--loading"}>
<div className="L_Ranking_title">RANKING</div>
{movies? this._renderMovies(): 'Loading!' }
</div>
Box office page
<L_MovieList/>
</div>
);
}
}
export default L_BoxOffice;
First of all set movies to be an empty array by default in the state.
constructor(props) {
super(props);
this.state = { movies: [] }
this._renderMovies = this._renderMovies.bind(this);
}
After that just render the movies:
<div className="L_Ranking_title">RANKING</div>
{this._renderMovies()}
</div>
Having an empty array as a default value, will remove the ternary operator usage and .map will always work, because by default the movies will be iterable.
Replace {movies? this._renderMovies(): 'Loading!' } with _this.renderMovies()
use arrow function
like you can directly call any function like this
_renderMovies = ()=>{
if(true){
}else{
return 'loading...' //just an example
}
}
render(){
{this._renderMovies()}
}

Why does my child React component need to be wrapped inside a function to detect parent state change?

This is my working code, and below it is what I'd like my code to look like but doesn't work:
I'd like my MainComponent's selectDay method to change TableComponent's urls for fetching data based on which day is selected
class MainComponent extends React.Component{
constructor(props) {
super(props);
this.state = {
urls: [{
appointmentsUrl: "/api/appointments",
callsUrl: "/api/calls"
}]
}
}
selectDay(day) {
if(day.key == 1) {
this.setState({
urls: [{
appointmentsUrl: "/api/appointments",
callsUrl: "/api/calls"
}]
})
} else {
this.setState({
urls: [{
appointmentsUrl: "/api/appointments2",
callsUrl: "/api/calls2"
}]
})
}
}
render() {
var calendarData = [//init some array of dates];
return (
<div>
<TopMenuComponent/>
<div className="contentContainer">
{
((urls)=>{
return(
<TableComponent key={urls[0].appointmentsUrl} appointmentsUrl={urls[0].appointmentsUrl} callsUrl={urls[0].callsUrl}/>
)
})(this.state.urls)
}
</div>
</div>
)
}
This works, but what I would like is something like is something like:
<TableComponent appointmentsUrl={this.state.appointmentsUrl} callsUrl={this.state.callsUrl}/>
And initialize the state as: this.state= {appointmentsUrl: "/api/appointments",
callsUrl: "/api/calls"}
If you have your state in the structure you represented in the question, and you want to use the first object in that array (assuming you have the destructuring syntax enabled):
function render() {
const [firstUrlObj] = this.state.urls
return (
<div>
<TopMenuComponent/>
<div className="contentContainer">
<TableComponent appointmentsUrl={firstUrlObj.appointmentsUrl} callsUrl={firstUrlObj.callsUrl}/>
</div>
</div>
)
}
If you want to render the whole array:
function render() {
const urls = this.state.urls
return (
<div>
<TopMenuComponent/>
<div className="contentContainer">
{ urls.map(urlObj => <TableComponent appointmentsUrl={urlObj.appointmentsUrl} callsUrl={urlObj.callsUrl}/>) }
</div>
</div>
)
}
If you have a single object in the array and you don't plan to have more, I suggest looking at the other answer.

Resources