React: component subscribing to redux store changes - reactjs

I am currently trying to use react-redux to subscribe to store changes for newsItems changes. My current component which is working but not listening for store changes as it should be looks like:
class Home extends Component {
state = { loading: false };
displayName = Home.name
render() {
let contents = this.renderContents(store.getState().newsItems);
return (
<div>
{contents}
</div>
);
}
renderContents = (newsItems) => {
var largeNewsItems = newsItems.splice(0, 2);
var firstNewsItem = largeNewsItems[0];
var secondNewsItem = largeNewsItems[1];
return (
<div>
<div>
<LargeNewsItem newsItem={firstNewsItem} />
</div>
<div>
<LargeNewsItem newsItem={secondNewsItem} />
</div>
</div>
);
}
}
export default Home;
when trying to update it to subscribe to teh store, I've made the following attempt:
class Home extends Component {
state = { loading: false };
displayName = Home.name
render(props) {
let contents = this.renderContents(props.newsItems);
return (
<div>
{contents}
</div>
);
}
renderContents = (newsItems) => {
var largeNewsItems = newsItems.splice(0, 2);
var firstNewsItem = largeNewsItems[0];
var secondNewsItem = largeNewsItems[1];
return (
<div>
<div>
<LargeNewsItem newsItem={firstNewsItem} />
</div>
<div>
<LargeNewsItem newsItem={secondNewsItem} />
</div>
</div>
);
}
}
const mapStateToProps = function(state) {
return {
newsItems: state.newsItems
}
}
export default connect(mapStateToProps)(Home);
which results in error:
TypeError: Cannot read property 'newsItems' of undefined
where I call props.newsItems.
What am I doing wrong here and how can I overcome it?
UPDATE:
it looks like I can overcome it by using:
`render() {
let contents = this.renderContents(this.props.newsItems);
return (
<div>
{contents}
</div>
);
}`
however my LargeNewsItem components will be passed null data once in a while. How can I overcome this and essentially "wait" until newsItems is populated

One approach could be to replace your render method as below .
`
render() {
const newsItems = this.props.newsItems;
if(!newsItems) {
return null;
}
let contents = this.renderContents(this.props.newsItems);
return (
<div>
{contents}
</div>
);
}`
this way if your newsitems is null you wont get an error and once the newsitems uddates your render method will be called again

Related

Create New Line after each Array Element

I'm very new to react and trying to teach it to myself. Trying to make an API call and loop through the records. I've been successful so far, however, I can't seem to figure out how to create a new line after each element that gets displayed. I'm sure it's something simple that I'm just missing. Can someone advise here?
import React from 'react';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
}
}
async componentDidMount() {
const url = "https://api.randomuser.me/?results=10";
const response = await fetch(url);
const data = await response.json();
this.setState({ person: data, loading: false, len: data.results.length });
}
render() {
let items = [];
for (let i = 0; i < this.state.len; i++) {
const item = this.state.person.results[i].name.title;
items.push(item);
}
return (
<div>
{this.state.loading || !this.state.person ? (
<div>loading...</div>
) : (
<div>
{items}
</div>
)}
</div>
);
}
}
You can use .map() function to loop over the array display it in within the tags.
return (
<div>
{this.state.loading || !this.state.person ? (
<div>loading...</div>
) : (
<div>
{
items.map((item) => (
<>
<span>{item}</span>
<hr />
</>
)
}
</div>
)}
</div>
);

Convert a React Class Component to a Function Component

Did I convert class component to function component correctly btw?
I don't know how to convert these code to function component tho, plz correct me
I don't necessarily need a code, just explain what I was wrong and what react knowledge I was missing
This is an original class component
const eventNames = ['onDragStart', 'onDrag', 'onDragEnd'];
function round5(value) {
return (Math.round(value * 1e5) / 1e5).toFixed(5);
}
export default class ControlPanel extends PureComponent {
renderEvent = eventName => {
const {events = {}} = this.props;
const lngLat = events[eventName];
return (
<div key={eventName}>
<strong>{eventName}:</strong> {lngLat ? lngLat.map(round5).join(', ') : <em>null</em>}
</div>
);
};
render() {
return (
<div className="control-panel">
<h3>Draggable Marker</h3>
<p>Try dragging the marker to another location.</p>
<div>{eventNames.map(this.renderEvent)}</div>
</div>
);
}
}
This is a code I converted to function component
function round5(value) {
return (Math.round(value * 1e5) / 1e5).toFixed(5);
}
const ControlPanel = (props) => {
const eventNames = ["onDragStart", "onDrag", "onDragEnd"];
function renderEvent(eventName) {
const { events } = props;
const lngLat = events[eventName];
return (
<div key={eventName}>
<strong>{eventName}:</strong>{" "}
{lngLat ? lngLat.map(round5).join(", ") : <em>null</em>}
</div>
);
}
return (
<div className="control-panel">
<h3>Draggable Marker</h3>
<p>Try dragging the marker to another location.</p>
<div>{eventNames.map(eventName => renderEvent(eventName))}</div>
</div>
);
};
export default ControlPanel;
you can reduce code a bit:
<div>{eventNames.map(renderEvent)}</div>
but renderEvent is not a react component so it has no this.props
You have to write renderEvent inside your functional component to access its props.
so in this case your component will be something like:
function round5(value) {
return (Math.round(value * 1e5) / 1e5).toFixed(5);
}
const ControlPanel = (props) => {
const renderEvent = (eventName) => {
const { events } = props;
const lngLat = events[eventName];
return (
<div key={eventName}>
<strong>{eventName}:</strong>{" "}
{lngLat ? lngLat.map(round5).join(", ") : <em>null</em>}
</div>
);
}
const eventNames = ["onDragStart", "onDrag", "onDragEnd"];
return (
<div className="control-panel">
<h3>Draggable Marker</h3>
<p>Try dragging the marker to another location.</p>
<div>{eventNames.map(eventName => renderEvent(eventName))}</div>
</div>
);
};
export default ControlPanel;

Why am I not able to check/uncheck checkboxes?

I am not able to check/uncheck checkboxes, but their checkmark value is being populated by the api and either checking them or leaving them unchecked. But for some reason I am not able to check/uncheck.
Tried putting them into different components and using onClick/onChange. Nothing works.
Parent component - PublishPage:
<div id="all-roles">
{this.props.listOfRoles ? (
<ListOfRoles checkedIDs={this.state.roleIDs} roles={this.props.listOfRoles} /> ) : null}
</div>
List of Roles
export default class ListOfRoles extends Component {
state = {
checkedIDs : []
}
static getDerivedStateFromProps(props) {
let checkedIDs = null;
if (props.checkedIDs) {
checkedIDs = props.checkedIDs
}
return { checkedIDs: checkedIDs };
}
render() {
const roles = this.props.roles[0].roles;
console.log(this.state);
return (
<div id="assign-to-role">
{roles.map(role => {
return (
<div>
<RoleCheckbox roleName={role.roleName} roleID={role.roleID} checkedArray={this.state.checkedIDs} />
</div>
);
})}
</div>
);
}
}
RoleCheckbox
export default class RoleCheckbox extends Component {
constructor(props) {
super(props);
this.state = {
checkboxDefault: true
}
this.handleCheckbox = this.handleCheckbox.bind(this);
}
static getDerivedStateFromProps(props) {
let checked = true;
let roleID = props.roleID;
let checkedArray = props.checkedArray;
if (checkedArray.indexOf(roleID) !== -1) {
checked = true
} else {
checked = false;
}
return { checkboxDefault: checked };
}
handleCheckbox(e) {
this.setState({
[e.target.name]: !this.state[e.target.name]
})
}
render() {
return (
<div>
<label> {this.props.roleName}
<input
type="checkbox"
name="checkboxDefault"
onChange={this.handleCheckbox}
checked={this.state.checkboxDefault} />
</label>
</div>
)
}
}
I want to be able to check/uncheck and also I want to be able to load them as checked if the api says they should be checked.
Actually what's happening here is that you are injecting the HTML with the boolean value from this.state.checkboxDefault which is initialized default to true inside RoleCheckbox component and the function that handles the change is not effecting the state. Change the state and it will reflect on the UI as well
handleCheckbox(e) {
this.setState({
checkboxDefault: !this.state.checkboxDefault
})
}

How to fix recursively updating state?

I am bulding an app using newsapi. i am facing two issue on my state. i fetch data using api and assign it to my state. and use it in my view.
Issue no 1
My view gets rendered before my app receives the data.
Issue no 2
When I try to update my state after a new fetch. it recursively updates the set of data again and again.
import React, {Component} from 'react';
import NewsComponent from './NewsComponent/NewsComponent'
class News extends Component {
state = {
displayStatus: false,
newsItems: []
};
toogleDisplayHandler = () => {
if(this.state.displayStatus===true){
this.setState({displayStatus:false})
}
else{
this.setState({displayStatus:true})
}
}
render(){
const NewsAPI = require('newsapi');
const newsapi = new NewsAPI('d6da863f882e4a1a89c5152bd3692fb6');
//console.log(this.props.keyword);
newsapi.v2.topHeadlines({
sources: 'bbc-news,abc-news',
q: this.props.keyword
}).then(response => {
//console.log(response)
response.articles.map(article => {
//console.log(article);
return(
//console.log(this.state.newsItems)
this.setState({
newsItems: [...this.state.newsItems, article],
})
//this.state.newsItems.push(article)
)
});
});
let Article = null;
Article = (
<div>
{
this.state.newsItems.map((news, index) => {
return (
<NewsComponent key={index}
title={news.title}
url={news.url}
description={news.description}
author={news.author}
publish={news.publishedAt}
image={news.urlToImage}
/>
)
})
}
</div>
)
return (
<div className="App">
{Article}
<button onClick={this.toogleDisplayHandler}>
{this.state.displayStatus === true ? "Hide Article" : "Display Articles"}
</button>
</div>
)
}
}
export default News;
Please help me to resolve this issue.
You should never setState in render as that would cause an infinite loop. Do it in componentDidMount or the constructor.
I would also recommend not using map for simply iterating over a list. Array.map is a function that is useful for returning an array that is constructed by iterating over another array. If you want to run some code for each element of an array use Array.forEach instead.
Like this:
import React, { Component } from "react";
import NewsComponent from "./NewsComponent/NewsComponent";
class News extends Component {
state = {
displayStatus: false,
newsItems: []
};
toogleDisplayHandler = () => {
if (this.state.displayStatus === true) {
this.setState({ displayStatus: false });
} else {
this.setState({ displayStatus: true });
}
};
componentDidMount = () => {
const NewsAPI = require("newsapi");
const newsapi = new NewsAPI("d6da863f882e4a1a89c5152bd3692fb6");
newsapi.v2
.topHeadlines({
sources: "bbc-news,abc-news",
q: this.props.keyword
})
.then(response => {
response.articles.forEach(article => {
this.setState({
newsItems: [...this.state.newsItems, article]
});
});
});
};
render() {
let Article = null;
Article = (
<div>
{this.state.newsItems.map((news, index) => {
return (
<NewsComponent
key={index}
title={news.title}
url={news.url}
description={news.description}
author={news.author}
publish={news.publishedAt}
image={news.urlToImage}
/>
);
})}
</div>
);
return (
<div className="App">
{Article}
<button onClick={this.toogleDisplayHandler}>
{this.state.displayStatus === true
? "Hide Article"
: "Display Articles"}
</button>
</div>
);
}
}
export default News;
1) You can add a check either your state has the data which you want to show on screen to render the view.
2) Please use ComponentDidMount React life cycle function to fetch data from an external source and update this data in the state. In the Render method, it will keep calling it recursively.

Reactjs - Props Is Lost While Using It In A Child Component TypeError:

I have the following code in react passes props from stateless component to state-full one and I get TypeError while running.
However, when I use props with same name the error goes away!
Your help would be appreciated in advance
import React, { Component } from 'react';
class App extends Component {
state = {
title:'xxxxxxx',
show:true,
SampleData:[object, object]
}
render() {
const {SampleData} = this.state.SampleData
return (
<div>
<SampleStateless list = {SampleData}/>
</div>
);
}
}
export default App;
const SampleStateless = (props) => {
const {list} = props
return (
<div>
<SampleStatefullComponent secondlist = {list} />
</div>
);
}
class SampleStatefullComponent extends Component {
state = {
something:''
}
render () {
const {secondlist} = this.props
console.log(secondlist);
// I get data correctly in console
const items = secondlist.map (item => return {some js})
//TypeError: secondlist is undefined
return (
<div>
{items}
</div>
)
}
}
You are doing map on a string but map works only on arrays. Take a look at corrected code
Also should be
const {SampleData} = this.state;
but not
const {SampleData} = this.state.SampleData;
Updated code
import React, { Component } from 'react';
class App extends Component {
state = {
title:'xxxxxxx',
show:true,
SampleData:[{'id': "01", "name": "abc"}, {'id': "02", "name": "xyz"}]
}
render() {
const {SampleData} = this.state;
return (
<div>
<SampleStateless list = {SampleData}/>
</div>
);
}
}
export default App;
const SampleStateless = (props) => {
const {list} = props
return (
<div>
<SampleStatefullComponent secondlist = {list} />
</div>
);
}
class SampleStatefullComponent extends Component {
state = {
something:''
}
render () {
const {secondlist} = this.props
console.log(secondlist);
// I get data correctly in console
const items = {secondlist && secondlist.map (item => item.name)}
return (
<div>
{items}
</div>
)
}
}

Resources