Cannot access store with connect in React-redux - reactjs

I am using React in my application. I am using connect to access the store, Briefly I have this code:
class MyComponent extends Component{
constructor(props){
super(props)
}
render(){
let components = this.props.components;
if(components.indexOf("SomeString")){
//some stuffs
}
return (
<SomeElement/>
)
}
}
const mapStateToProps = state => {
return {
components: state.someReducer.components
}
}
export default connect(mapStateToProps)(MyComponent)
Components is an array of strings, if I print with console.log(this.props.components) inside the render function, the I am able to see the array in the browser console. However, if I try to find a value inside that array using indexOf then I get this error:
TypeError: components is undefined
So, What am I missing? I tried many things without result

At first cycle, this.props.components is undefined. You can bypass it using
render(){
if(this.props.components) {
let components = this.props.components;
if(components!=null || components!=undefined){ //this will check if components is null or undefined
if(components.indexOf("SomeString")){
//some stuffs
}}
return (
<SomeElement/>
)
}

You need to check for the this.props.components like so:
render(){
if(this.props.components) {
let components = this.props.components;
if(components.indexOf("SomeString")){
//some stuffs
}
return (
<SomeElement/>
)
} else {
return(<div>Loading...</div>)
}
}

Related

Reactjs IF statement

I have a component I call that is a passed a recordID and returns the text associated to the Id. 33 should = Tower
will render "Tower" on the screen. All good, but...
When I try to use the component in the following IF statement it does not work.
...
if (<GetAssetTypeNameComponent datafromparent = {assettype_assettypeId}/> === "Tower")
{
this.props.history.push(`/add-assetstower/${assetsid}/${this.props.match.params.sitemasterid}`);
}
Using the passed parameter does work if I change the code to:
...
if (assettype_assettypeId === "33")
{
this.props.history.push(`/add-assetstower/${assetsid}/${this.props.match.params.sitemasterid}`);
}
...
What am I doing wrong?
Rob
Component Code that needs to be a Function....
...
class GetAssetTypeNameComponent extends Component {
constructor (props){
super(props)
this.state = {
assettype:[]
}
}
componentDidMount()
{
AssetTypeService.getAssetTypeById(this.props.datafromparent).then( (res) =>{
let assettype = res.data;
this.setState({isLoading:false});
this.setState({
assettypeName: assettype.assettypeName,
assettypeType: assettype.assettypeType
});
});
}
render() {
return (
<div>
{this.state.assettypeName}
</div>
);
}
}
export default GetAssetTypeNameComponent;
...
Following Function code compiles:
...
import React, { useState} from 'react';
import AssetTypeService from './AssetTypeService'
const GetAssetTypeNameFunction = (props) =>{
// destructuring
const { assettype_assettypeId } = props;
const [assetType,setAssetType] = useState()
AssetTypeService.getAssetTypeById(assettype_assettypeId).then( (res) =>
setAssetType(res.data));
const arrayMap = assetType.map((post)=>{
return(
<ul>
{post.assettypeName}
</ul>
);})
return (
{arrayMap}
);
}
export default GetAssetTypeNameFunction;
...
Get execution error:
I think because I calling the function from within an eventHandler:
...
editAssets(assetsid,assettype_assettypeId){ if (GetAssetTypeNameFunction(assettype_assettypeId) === "Tower") { this.props.history.push(/add-assetstower/${assetsid}/${this.props.match.params.sitemasterid}); }]
...
----- Error: Invalid hook call. Hooks can only be called inside of the body of a function component. I am responding to a onClick in a list to route to a specific component based on the function $
How do I get around this?
A component renders content to be displayed in the page. The retuned value of rendering a component is a tree of nodes that contain your content. All this means that <GetAssetTypeNameComponent> may contain the text content Tower, but it is not equal to the string "Tower". It just doesn't make any sense to render a component as the test for a conditional like this.
In React you want to use logic to tell react how to render. You do not want to render and then use the result in your logic.
It's hard to give advice on the best way to fix that with so little code, but maybe you want a a simple function to coverts the id into some text for you.
function getAssetName(id) {
return someLogicSomewhere(id).result.orWhatever
}
And now you can do something like:
if (getAssetName(assettype_assettypeId) === 'Tower')
{
this.props.history.push(
`/add-assetstower/${assetsid}/${this.props.match.params.sitemasterid}`
);
}

Calling props from a container

I am a little confused on the idea of using props in the context I am using for my React app. In my component, I need to check if the value of a certain prop (props.companyCode) matches a certain string, and only then will it print out a <p> of what I need. Below is what I have for calling the prop in the component:
Components/CompanyContact.jsx
class CompanyContact extends React.Component {
help() {
if (this.props.companyInfoList.companyCode === '1234') {
return <p>something</p>;
}
return <p>somethingelse</p>;
}
render() {
const help = this.help();
return (
<div>
{help};
</div>
)}}
export default CompanyContact;
And this is what I have for the container:
Container/InfoContainer.jsx
class InfoContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
companyInfoList: null,
};
}
async componentWillMount() {
const companyInfoCachedData = CachingService.getData('companyInfoList');
if (companyInfoCachedData) {
this.setState({ companyInfoList: companyInfoCachedData });
return;
}
}
async getCompanyInfo(accessToken) {
try {
const companyProfileResponse = await requestAWSGet('api/company-profile', undefined, accessToken);
CachingService.setData('companyInfoList', companyProfileResponse);
this.setState({ companyInfoList: companyProfileResponse });
} catch (err) {
throw err;
}
}
render() {
return (
<CompanyContact companyInfoList={this.state.companyInfoList} />
);
}
}
export default InfoContainer;
Nothing is returned when I run the application and I believe it's because I'm not calling the prop correctly in my component but I am unsure as to how to go about fixing it. I'm fairly new to working with props so still trying to get my bearings.
I'm assuming you are getting an error somewhere because of this not having props and this.props.companyInfoList.companyCode trying to access a property on a non object. this.props.companyInfoList is initially set to null so accessing a property on it will break.
A few strategies to fix the problem:
Default it to an empty object
this.state = {
companyInfoList: {},
}
Block the rendering of the component until it has a value:
if (this.state.companyInfoList) {
return (
<CompanyContact companyInfoList={this.state.companyInfoList} />
);
} else {
return null;
}
Check that the prop is an object and has the key companyCode on it:
if (this.props.companyInfoList &&
this.props.companyInfoList.companyCode &&
this.props.companyInfoList.companyCode === '1234') {
In addition, this will be in the wrong context and the changes above will most likely no be enough. Try changing to an arrow function like this:
help = () => {
// your code here
}
I would personally refactor that component logic and directly use the prop value inside the render method like:
class CompanyContact extends React.Component {
render() {
const { companyInfoList } = this.props;
return companyInfoList && companyInfoList.companyCode === '1234' ? (
<p>something</p>
) : (
<p>somethingelse</p>
)
}
}
export default CompanyContact;

React map redux state to create multiple components within a component

I am using the react-redux-mapbox-gl library. I have an array of spots that I want to map in order to create multiple overlay components within the Mapbox Component. However on trying to map the array I always get an undefined error. I am new to React/Redux so am not sure what the issue is.
My Component Below:
import React from 'react';
import Mapbox from 'react-redux-mapbox-gl';
import SpotsOverlay from './SpotsOverlay'
const mapStateToProps = state => ({
spots: state.homemap.spots
})
class HomeMap extends React.Component {
render(){
return (
<Mapbox
mapboxgl={mapboxgl}
accessToken={mapAccessToken}
getMap={this.getMap}
style={this.mapStyle}
options={this.mapOptions}
>
{
this.props.spots.map(spot =>{
return (
<SpotsOverlay
overlay={this.overlay}
key={spot.id}/>
);
})
}
</Mapbox>
);
}
}
Do the mapping outside of the return method might help.
class HomeMap extends React.Component {
render(){
let spots = [];
if(this.props.spots) {
spots = this.props.spots.map(spot =>{
return (
<SpotsOverlay
overlay={this.overlay}
key={spot.id}/>
);
});
}
return (
<Mapbox
mapboxgl={mapboxgl}
accessToken={mapAccessToken}
getMap={this.getMap}
style={this.mapStyle}
options={this.mapOptions}
>
{spots}
</Mapbox>
);
}
}
As #MayankShukla said in his comment, the reason why this works better is that
initially reducer state is {}, so state.homemap.spots will be undefined and when you were using map of undefined

Cannot call a method in the class

Getting started with React and Redux and I have a method in my class to create new lines in a piece of text:
class JobPage extends React.Component {
componentWillMount() {
this.props.dispatch(fetchJob(this.props.match.params.slug))
}
getMultiLineText(text) {
text.split('\n').map((item, key) => {
return <span key={key}>{item}<br/></span>
})
}
render() {
const { job } = this.props;
return(
{this.getMultiLineText(job.desc)}
)
}
}
Within my render() method I try and call it using the following but the param is not passed. It's null:
{this.getMultiLineText(job.desc)}
If I just do...
{job.desc}
it prints the description.
The value is probably null during the initial render.
You can solve it for example as
{this.getMultiLineText(job.desc || '')}
You can also fix it in your reducer.

Creating a non-rendered wrapper component in react.js

I'm wanting to create a React component that does a security check and if that passes it'll render out the children of it, if it fails then it won't render anything.
I've scaffolded out a component like so:
var RolesRequired = React.createClass({
permitted: roles => ...,
render: function () {
if (!this.permitted(this.props.roles)) {
return null;
}
return this.props.children;
}
});
The usage I was planning would be like this:
<RolesRequired roles={['admin']}>
<h1>Welcome to the admin</h1>
<div>
Admin stuff here
</div>
</RolesRequired>
How would you return all the children from the RolesRequired component?
I came up with this solution:
var RolesRequired = React.createClass({
permitted: roles => ...,
render: function () {
if (!this.permitted(this.props.roles)) {
return null;
}
return <div>{this.props.children}</div>;
}
});
What I'm doing is wrapping the children being returned in a <div> but I'm having to add an unwanted/unneeded DOM element to achieve it.
I think higher order components (HOC) are also a good candidate for this. You can basically wrap any component in HOC that defines some behaviour and decides if it should render a wrappe.
Nicest way to do this would be if you're using a ES2015 transpiler with some ES2016 features enabled (namely decorators):
function withRoles(roles) {
return function(Component) {
return class ComponentWithRoles extends React.Component {
constructor(props) {
super(props)
// Not sure where the data to get your roles about current user?
// from but you could potentially to that here if I'm getting your point
// and also setup listeners
this.state = { currentUser: 'admin' }
}
validateRoles() {
// you have access to the ``roles`` variable in this scope
// you can use it to validate them.
return true;
}
render() {
if (this.validateRoles()) {
return <Component {...this.props} />;
)
} else {
return <div>Nope...</div>;
}
}
}
}
}
// You can then use this on any component as a decorator
#withRoles({ showOnlyFor: [ 'admin' ] })
class AdminOnlyComponent extends React.Component {
render() {
return <div> This is secert stuff </div>
}
}
I've used ES2016 features because I think it's nicer to get the point across but you can implement that with just a simple function wrapping, here's a gist by one of the React core members on the topic of HOC:
https://gist.github.com/sebmarkbage/ef0bf1f338a7182b6775

Resources