How to use switch statement inside a React component? - reactjs

I have a React component, and inside the render method of the component I have something like this:
render() {
return (
<div>
<div>
// removed for brevity
</div>
{ switch(...) {} }
<div>
// removed for brevity
</div>
</div>
);
}
Now the point is that I have two div elements, one at the top and one at the bottom, that are fixed. In the middle I want to have a switch statement, and according to a value in my state I want to render a different component. So basically, I want the two div elements to be fixed always, and just in the middle to render a different component each time. I'm using this to implement a multi-step payment procedure). Though, as is the code currently it doesn't work, as it gives me an error saying that switch is unexpected. Any ideas how to achieve what I want?

Try this, which is way cleaner too: Get that switch out of the render in a function and just call it passing the params you want. For example:
renderSwitch(param) {
switch(param) {
case 'foo':
return 'bar';
default:
return 'foo';
}
}
render() {
return (
<div>
<div>
// removed for brevity
</div>
{this.renderSwitch(param)}
<div>
// removed for brevity
</div>
</div>
);
}

In contrast to other answers, I would prefer to inline the "switch" in the render function. It makes it more clear what components can be rendered at that position. You can implement a switch-like expression by using a plain old javascript object:
render () {
return (
<div>
<div>
{/* removed for brevity */}
</div>
{
{
'foo': <Foo />,
'bar': <Bar />
}[param]
}
<div>
{/* removed for brevity */}
</div>
</div>
)
}

That's happening, because switch statement is a statement, but here javascript expects an expression.
Although, it's not recommended to use switch statement in a render method, you can use self-invoking function to achieve this:
render() {
// Don't forget to return a value in a switch statement
return (
<div>
{(() => {
switch(...) {}
})()}
</div>
);
}

I did this inside the render() method:
render() {
const project = () => {
switch(this.projectName) {
case "one": return <ComponentA />;
case "two": return <ComponentB />;
case "three": return <ComponentC />;
case "four": return <ComponentD />;
default: return <h1>No project match</h1>
}
}
return (
<div>{ project() }</div>
)
}
I tried to keep the render() return clean, so I put my logic in a 'const' function right above. This way I can also indent my switch cases neatly.

I'm not a big fan of any of the current answers, because they are either too verbose, or require you to jump around the code to understand what is going on.
I prefer doing this in a more react component centred way, by creating a <Switch/>. The job of this component is to take a prop, and only render children whose child prop matches this one. So in the example below I have created a test prop on the switch, and compared it to a value prop on the children, only rendering the ones that match.
Example:
const Switch = props => {
const { test, children } = props
// filter out only children with a matching prop
return children.find(child => {
return child.props.value === test
})
}
const Sample = props => {
const someTest = true
return (
<Switch test={someTest}>
<div value={false}>Will display if someTest is false</div>
<div value={true}>Will display if someTest is true</div>
</Switch>
)
}
ReactDOM.render(
<Sample/>,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>
You can make the switch as simple or as complex as you want. Don't forget to perform more robust checking of the children and their value props.

A way to represent a kind of switch in a render block, using conditional operators:
{(someVar === 1 &&
<SomeContent/>)
|| (someVar === 2 &&
<SomeOtherContent />)
|| (this.props.someProp === "something" &&
<YetSomeOtherContent />)
|| (this.props.someProp === "foo" && this.props.someOtherProp === "bar" &&
<OtherContentAgain />)
||
<SomeDefaultContent />
}
It should be ensured that the conditions strictly return a boolean.

lenkan's answer is a great solution.
<div>
{{ beep: <div>Beep</div>,
boop: <div>Boop</div>
}[greeting]}
</div>
If you need a default value, then you can even do
<div>
{{ beep: <div>Beep</div>,
boop: <div>Boop</div>
}[greeting] || <div>Hello world</div>}
</div>
Alternatively, if that doesn't read well to you, then you can do something like
<div>
{
rswitch(greeting, {
beep: <div>Beep</div>,
boop: <div>Boop</div>,
default: <div>Hello world</div>
})
}
</div>
with
function rswitch (param, cases) {
if (cases[param]) {
return cases[param]
} else {
return cases.default
}
}

Although this is yet another way to do it, if you have gone all-in on hooks, you could take advantage of useCallback to produce a function that is only recreated when necessary.
Let's say you have a component which should be rendered according to a status prop. With hooks, you could implement this as follows:
const MyComponent = ({ status }) => {
const renderContent = React.useCallback(() => {
switch(status) {
case 'CONNECTING':
return <p className="connecting">Connecting...</p>;
case 'CONNECTED':
return <p className="success">Connected Successfully!</p>
default:
return null;
}
}, [status]);
return (
<div className="container">
{renderContent()}
</div>
);
};
I like this because:
It's obvious what is going on - a function is created, and then later called (the immediately invoked anonymous function method looks a little odd, and can potentially confuse newer developers)
The useCallback hook ensures that the renderContent callback is reused between renders, unless the depedency status changes
The renderContent function uses a closure to access the necessary props passed in to the component. A separate function (like the accepted answer) requires the passing of the props into it, which can be burdensome (especially when using TypeScript, as the parameters should also be typed correctly)

You can do something like this.
<div>
{ object.map((item, index) => this.getComponent(item, index)) }
</div>
getComponent(item, index) {
switch (item.type) {
case '1':
return <Comp1/>
case '2':
return <Comp2/>
case '3':
return <Comp3 />
}
}

function Notification({ text, status }) {
return (
<div>
{(() => {
switch (status) {
case 'info':
return <Info text={text} />;
case 'warning':
return <Warning text={text} />;
case 'error':
return <Error text={text} />;
default:
return null;
}
})()}
</div>
);
}

You can't have a switch in render. The psuedo-switch approach of placing an object-literal that accesses one element isn't ideal because it causes all views to process and that can result in dependency errors of props that don't exist in that state.
Here's a nice clean way to do it that doesn't require each view to render in advance:
render () {
const viewState = this.getViewState();
return (
<div>
{viewState === ViewState.NO_RESULTS && this.renderNoResults()}
{viewState === ViewState.LIST_RESULTS && this.renderResults()}
{viewState === ViewState.SUCCESS_DONE && this.renderCompleted()}
</div>
)
If your conditions for which view state are based on more than a simple property – like multiple conditions per line, then an enum and a getViewState function to encapsulate the conditions is a nice way to separate this conditional logic and cleanup your render.

I really liked the suggestion in https://stackoverflow.com/a/60313570/770134, so I adapted it to Typescript like so
import React, { FunctionComponent } from 'react'
import { Optional } from "typescript-optional";
const { ofNullable } = Optional
interface SwitchProps {
test: string
defaultComponent: JSX.Element
}
export const Switch: FunctionComponent<SwitchProps> = (props) => {
return ofNullable(props.children)
.map((children) => {
return ofNullable((children as JSX.Element[]).find((child) => child.props['value'] === props.test))
.orElse(props.defaultComponent)
})
.orElseThrow(() => new Error('Children are required for a switch component'))
}
const Foo = ({ value = "foo" }) => <div>foo</div>;
const Bar = ({ value = "bar" }) => <div>bar</div>;
const value = "foo";
const SwitchExample = <Switch test={value} defaultComponent={<div />}>
<Foo />
<Bar />
</Switch>;

import React from 'react';
import ListView from './ListView';
import TableView from './TableView';
function DataView({
currView,
data,
onSelect,
onChangeStatus,
viewTodo,
editTodo,
deleteTodo,
}) {
return (
<div>
{(function () {
switch (currView) {
case 'table':
return (
<TableView
todos={data}
onSelect={onSelect}
onChangeStatus={onChangeStatus}
viewTodo={viewTodo}
editTodo={editTodo}
deleteTodo={deleteTodo}
/>
);
case 'list':
return (
<ListView
todos={data}
onSelect={onSelect}
onChangeStatus={onChangeStatus}
viewTodo={viewTodo}
editTodo={editTodo}
deleteTodo={deleteTodo}
/>
);
default:
break;
}
})()}
</div>
);
}
export default DataView;

Improved a bit from
Matt Way's answer.
export const Switch = ({ test, children }) => {
const defaultResult = children.find((child) => child.props.default) || null;
const result = children.find((child) => child.props.value === test);
return result || defaultResult;
};
export const Case = ({ children }) => children;
const color = getColorFromTheMostComplexFnEver();
<Switch test={color}>
<Case value="Green">Forest</Case>
<Case value="Red">Blood</Case>
<Case default>Predator</Case>
</Switch>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

How about:
mySwitchFunction = (param) => {
switch (param) {
case 'A':
return ([
<div />,
]);
// etc...
}
}
render() {
return (
<div>
<div>
// removed for brevity
</div>
{ this.mySwitchFunction(param) }
<div>
// removed for brevity
</div>
</div>
);
}

Switch-Case statement within React Component could be used as follows:
<div id="time-list">
{
(() => {
switch (groupByFilterId) {
case 0:/*Case 0 */
return (
<div>Case 0</div>
)
break;
case 1: /*Case 1 */
return (
<div>Case 1</div>
)
break;
case 2:/*Case 2 */
return (
<div>Case 2</div>
)
break;
}
})()}
</div>

make it easy and just use many if statements.
for example:
<Grid>
{yourVar==="val1"&&(<> your code for val1 </>)}
{yourVar==="val2"&&(<> your code for val2 </>)}
.... other statments
</Grid>

I converted accepted answer to arrow functional component solution and saw James provides similar answer and one can get error not defined. So here is the solution:
const renderSwitch = (param) => {
switch (param) {
case "foo":
return "bar";
default:
return "foo";
}
};
return (
<div>
<div></div>
{renderSwitch(param)}
<div></div>
</div>
);

Here is a full working example using a button to switch between components
you can set a constructor as following
constructor(props)
{
super(props);
this.state={
currentView: ''
}
}
then you can render components as following
render()
{
const switchView = () => {
switch(this.state.currentView)
{
case "settings": return <h2>settings</h2>;
case "dashboard": return <h2>dashboard</h2>;
default: return <h2>dashboard</h2>
}
}
return (
<div>
<button onClick={(e) => this.setState({currentView: "settings"})}>settings</button>
<button onClick={(e) => this.setState({currentView: "dashboard"})}>dashboard</button>
<div className="container">
{ switchView() }
</div>
</div>
);
}
}
As you can see I am using a button to switch between states.

I know I'm a bit late to the party, but I think this implementation might help
You can render the components using conditional operators instead
If you had the following switch statement
switch(value) {
case CASE1:
return <Case1Component/>
case CASE2:
return <Case2Component/>
case CASE3:
return <Case3Component/>
default:
return <DefaultComponent/>
}
You can convert it to react component like so
const cases = [CASE0, CASE1, CASE2]
// Reminds me of 'react-router-dom'
return (
<div>
{value === cases[0] && <Case0Component/>}
{value === cases[1] && <Case1Component/>}
{value === cases[2] && <Case2Component/>}
{!cases.includes(value) && <DefaultComponent/>}
</div>
)

I am using this helper that allows me to have switch statements in JSX
// in helpers folder
const switchTrue = (object) => {
const { default: defaultValue, ...rest } = object;
const obj = { default: defaultValue, ...rest };
const result = Object.keys(obj).reduce((acc, cur) => {
return {
...acc,
[cur === 'default' ? 'true' : cur]: obj[cur],
};
}, {});
return result['true'];
};
const Sample = () => {
const isDataLoading = false;
return (
<div>
{
switchTrue({
[`${isDataLoading}`]: <div>Loading</div>,
[`${!isDataLoading}`]: <div>Data Ready</div>,
default: <div>Default</div>,
})
}
</div>
)
}
ReactDOM.render(
<Sample/>,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>

This helper should do the trick.
Example Usage:
{componentSwitch(3, (switcher => switcher
.case(1, () =>
<p>It is one</p>
)
.case(2, () =>
<p>It is two</p>
)
.default(() =>
<p>It is something different</p>
)
))}
Helper:
interface SwitchCases<T> {
case: (value: T, result: () => React.ReactNode) => SwitchCases<T>;
default: (result: () => React.ReactNode) => SwitchCases<T>;
}
export function componentSwitch<T>(value: T, cases: (cases: SwitchCases<T>) => void) {
var possibleCases: { value: T, result: () => React.ReactNode }[] = [];
var defaultResult: (() => React.ReactNode) | null = null;
var getSwitchCases: () => SwitchCases<T> = () => ({
case: (value: T, result: () => React.ReactNode) => {
possibleCases.push({ value: value, result });
return getSwitchCases();
},
default: (result: () => React.ReactNode) => {
defaultResult = result;
return getSwitchCases();
},
})
// getSwitchCases is recursive and will add all possible cases to the possibleCases array and sets defaultResult.
cases(getSwitchCases());
// Check if one of the cases is met
for(const possibleCase of possibleCases) {
if (possibleCase.value === value) {
return possibleCase.result();
}
}
// Check if the default case is defined
if (defaultResult) {
// Typescript wrongly assumes that defaultResult is always null.
var fixedDefaultResult = defaultResult as (() => React.ReactNode);
return fixedDefaultResult();
}
// None of the cases were met and default was not defined.
return undefined;
}

We can do this directly using useCallback
const renderContent = useCallback(() => {
switch (sortState) {
case 'one':
return 'ONE';
case 'two':
return 'TWO';
case 'three':
return 'THREE';
case 'four':
return 'FOUR';
default:
return 'ONE';
}
}, [sortState]);
This is to be used inside the jsx
<div>Sort:{renderContent()}</div>

This answer is specifically intended to address this "duplicate" question, by #tonyfat, regarding how to use conditional expressions to handle the same task.
Avoiding statements here seems like more trouble than it's worth, but this script does the job as the snippet demonstrates:
// Runs tests
let id = 0, flag = 0;
renderByFlag(id, flag); // jobId out of range
id = 1; // jobId in range
while(++flag < 5){ // active flag ranges from 1 to 4
renderByFlag(id, flag);
}
// Defines a function that chooses what to render based on two provided values
function renderByFlag(jobId, activeFlag){
jobId === 1 ? (
activeFlag === 1
? render("A (flag = 1)")
: activeFlag === 2
? render("B (flag = 2)")
: activeFlag === 3
? render("C (flag = 3)")
: pass(`flag ${activeFlag} out of range`)
)
: pass(`jobId ${jobId} out of range`)
}
// Defines logging functions for demo purposes
function render(val){ console.log(`Rendering ${val}`); }
function pass(reason){ console.log(`Doing nothing (${reason})`) }

const [route, setRoute] = useState(INITIAL_ROUTE)
return (
<RouteContext.Provider value={{ route, setRoute }}>
{(() => {
switch (route) {
case Route.Home:
return <PopupHomePage />
case Route.App:
return <PopupAppPage />
default:
return null
}
})()}
</RouteContext.Provider>

This is another approach.
render() {
return {this[`renderStep${this.state.step}`]()}
renderStep0() { return 'step 0' }
renderStep1() { return 'step 1' }

Related

React 17 Use Conditional Switch in Function Return

I am trying to do condition rendering in the latest version of React 17, which does not have Render, only Return.
How Can I get this switch statement working? Currently its not returning anything from my switch statement.
const Search = (props) => {
let test = 'foo';
return (
<div>
{function() {
switch(test) {
case 'foo':
return 'foo12345';
case 'bar':
return 'bar67890';
default:
return 'aaaa';
}
}
}
</div>
)
}
Using functions over class based method. Will have to use 8-10 condition switch statement in future,
Right now, you're just declaring a function, but never executing it. Simplest tweak would be:
return (
<div>
{
(function () {
switch (test) {
case 'foo':
return 'foo';
default:
return 'bar';
}
})()
}
</div>
)
But a much better approach would be:
return (
<div>
{
test === 'foo' ? 'foo' : 'bar'
}
</div>
)
If you have lots of possibilities, I'd recommend an object or Map, it's much more concise and less error-prone than switch.
const obj = {
foo: 'foo12345',
bar: 'bar67890',
// etc
};
return (
<div>
{
obj[test] || 'aaaa'
}
</div>
)

Slate editor active state always false using isBlockActive from examples

I have created an editor from composing a couple of slates examples together, namely https://www.slatejs.org/examples/richtext and https://www.slatejs.org/examples/links
However when I added these the active state for all block level nodes it doesn't work at all, meaning I can't toggle list items on and off, anchor links end up getting nested etc.
The broken code seems to be these lines, coming from isBlockActive function in all the examples:
const isBlockActive = (editor, format) => {
const [match] = Editor.nodes(editor, {
match: n => {
console.log('n.type', n.type);
return n.type === format;
},
});
return !!match;
};
match is always undefined no matter where my cursor is located.
I am running latest on all the packages currently 0.58.1.
Below is my toggleBlock function I also took from the examples that uses the isBlockActive function to work out the toggling logic.
const toggleBlock = (editor, format) => {
const isActive = isBlockActive(editor, format);
const isList = LIST_TYPES.includes(format);
Transforms.unwrapNodes(editor, {
match: n => LIST_TYPES.includes(n.type),
split: true,
});
Transforms.setNodes(editor, {
type: isActive ? 'paragraph' : isList ? 'list-item' : format,
});
if (!isActive && isList) {
const block = { type: format, children: [] };
Transforms.wrapNodes(editor, block);
}
};
Has anyone run into this problem before, perhaps the codebase is not in sync with the examples and Editor.nodes isn't recommended anymore?
All inline formatting options work as the example uses a different approach:
const isMarkActive = (editor, format) => {
const marks = Editor.marks(editor);
return marks ? marks[format] === true : false;
};
Also here is my toolbar and renderElement functions if it helps:
<Slate editor={editor} value={value} onChange={handleChange}>
<div className={styles.toolbar}>
<MarkButton format="bold" icon="bold" />
<MarkButton format="italic" icon="italic" />
<MarkButton format="underline" icon="underline" />
<MarkButton format="code" icon="code" />
<BlockButton format="heading-one" icon="h1" />
<BlockButton format="heading-two" icon="h2" />
<BlockButton format="heading-three" icon="h3" />
<BlockButton format="quote" icon="quote-left" />
<BlockButton format="numbered-list" icon="list-ol" />
<BlockButton format="bulleted-list" icon="list-ul" />
<BlockButton format="break" icon="horizontal-rule" />
<LinkButton />
</div>
...
const Element = ({ attributes, children, element }) => {
switch (element.type) {
case 'quote':
return <blockquote {...attributes}>{children}</blockquote>;
case 'code':
return (
<pre>
<code {...attributes}>{children}</code>
</pre>
);
case 'heading-one':
return <h1 {...attributes}>{children}</h1>;
case 'heading-two':
return <h2 {...attributes}>{children}</h2>;
case 'heading-three':
return <h3 {...attributes}>{children}</h3>;
case 'heading-four':
return <h4 {...attributes}>{children}</h4>;
case 'heading-five':
return <h5 {...attributes}>{children}</h5>;
case 'heading-six':
return <h6 {...attributes}>{children}</h6>;
case 'list-item':
return <li {...attributes}>{children}</li>;
case 'numbered-list':
return <ol {...attributes}>{children}</ol>;
case 'bulleted-list':
return <ul {...attributes}>{children}</ul>;
case 'link':
return (
<a {...attributes} href={element.url}>
{children}
</a>
);
default:
return <p {...attributes}>{children}</p>;
}
};
Editor.nodes() returns an iterator.
You'll need to change the isBlockActive function to:
const isBlockActive = (editor, format) => {
const nodes = Editor.nodes(editor, {
match: n => n.type === format,
})
return !!nodes.next().value
}

Cannot read property function of null

Hello I'm trying to test a function from a function but tells me this error.
TypeError: Cannot read property 'getNextServiceIconStyle' of null
Code
function IssueNextServiceIcon ({ nextService, intl }) {
return (
<div styles[getNextServiceIconStyle(nextService.approaching, nextService.overDue)])}>
<NextServiceIcon className={styles['icon']} />
</div>
)
function getNextServiceIconStyle (approaching, overDue) {
if (overDue) {
return 'next-service-overdue'
}
else if (approaching) {
return 'next-service-approaching'
}
return ''
}
}
Test
test('should', () => {
const wrapper = shallow(<IssueNextServiceIcon {...mockPropsForComponent} />)
const instance = wrapper.instance()
const expectedResult = 'next-service-overdue'
expect(instance.getNextServiceIconStyle(true, false)).toEqual(expectedResult)
})
Any suggestion for the test?
There a few syntax errors and unclosed braces, but if I understood your intent correctly, you'd do smth like this:
function IssueNextServiceIcon({ nextService, intl }) {
function getNextServiceIconStyle(approaching, overDue) {
if (overDue) {
return "next-service-overdue";
} else if (approaching) {
return "next-service-approaching";
}
return "";
}
const styleKey = getNextServiceIconStyle(
nextService.approaching,
nextService.overDue
);
return (
// Or if you need to pass className: className={styles[styleKey]}
<div styles={styles[styleKey]}>
<NextServiceIcon className={styles["icon"]} />
</div>
);
}
Regarding the test, you cannot use wrapper.instance() because this is not a class component. What you could do is to render your component and check that it has proper styles applied:
test('it should have correct styling', () => {
const wrapper = shallow(<IssueNextServiceIcon {...mockPropsForComponent} />)
expect(component.find('NextServiceIcon').prop('style')).toHaveProperty('color', 'red') // test for the actual css you have
})

React render from a loop

I'm trying to do something very simple but its not playing well with my code. I can see it render but only 3 times and not 9
const renderTempBoxes = () => {
for (var i = 0; i < 10; i++) {
console.log('i = ', i);
return <div className={styles.box} key={i} />;
}
};
const Component = () => {
return (
{renderTempBoxes()}
)
}
This doesn't even work, which is overkill to use an array when I just want 9 boxes to render.
UPDATE:
const Component = () => {
return (
<div>
{
[...Array(10)].map((x, i) => {
console.log('i = ', i);
return <div className={styles.box} key={i} />;
})
}
</div>
)
}
The first issue is that you simply cannot return individual elements from within the for loop like that. This is not specific to React, this is simply a JavaScript issue. Instead you can try something like this using Array.from to map an array of elements:
const renderTempBoxes = () => Array.from({ length: 10 }).map((v, i) =>
<div className={styles.box} key={i}>{i}</div>
);
Or simply the for loop with Array.prototype.push to generate an array of elements and return it:
const renderTempBoxes = () => {
let els = [];
for (let i = 0; i < 10; i++) {
els.push(<div className={styles.box} key={i}>{i}</div>);
}
return els;
};
Rendering the elements:
const Component = () => {
return (
<div>
{renderTempBoxes()}
</div>
)
}
Or with React.Fragment to forgo the wrapping extra node:
const Component = () => {
return (
<React.Fragment>
{renderTempBoxes()}
</React.Fragment>
)
}
The second issue with your example is that <div /> isn't going to really render anything, it's not a void/self-closing element such as <meta />. Instead you would need to do return the div element as <div className={styles.box} key={i}>{whatever}</div>.
Regarding the syntax [...Array(10)], there must be an Webpack in terms of how it handles/transpiles Array(10), [...Array(10)], [...new Array(10)], or even `[...new Array(10).keys()]. Either of the approaches described in the answer should solve your issue.
I've created a StackBlitz to demonstrate the functionality.
When trying to render multiple times the same components use an array an map over it.
export default class MyComp extends Component {
constructor(props) {
super(props)
this.state = {
array: [{key: 1, props: {...}}, {key: 2, props: {...}, ...]
}
}
render () {
return (
<div>
{this.state.array.map((element) => {
return <div key={element.key} {...element.props}>
})}
</div>
)
}
}
Remember to always set a unique key to every component you render

How can I use if statement in render function?

Below is my render method.
I want to loop through toptracks only if it is not null.
But I am not able to get this working.
render() {
console.log('galler props',this.props);
const { toptracks } = this.props;
return (
<div>
if(this.props.toptracks !== null){
{toptracks.map((toptracks,k) => {
const trackImg =toptracks.track[0].image[0]['#text'];
return (
<div
key = {k}
className ="track"
>
<img
src={trackImg}
className="trackImg"
alt="track"
/>
<p className="tracl-text">
{track.name}
</p>
</div>
)
}
)
}
}
</div>
)
}
Using an if is rather simple if you decide for an early return:
render() {
const { toptracks } = this.props;
if (!toptracks) { // this will handle both null and undefined
return <div/>;
}
return (
...
);
}
However, there are simpler ways to do that. Note that in your case having no value is the same as having an empty value. Why not default to an empty value instead then?
render() {
const toptracks = this.props.toptracks || [];
// no change needed here
}
The same can be done by defining defaultProps for your component.
static defaultProps = {
toptracks: []
};
You can't have an if statement within your render-return statement. You can make use of ternary statement like
render() {
const { toptracks } = this.props;
return (
<div>
{this.props.toptracks ? toptracks.map((toptracks,k) => {
const trackImg =toptracks.track[0].image[0]['#text'];
return (
<div key = {k} className ="track">
<img
src={trackImg}
className="trackImg"
alt="track"
/>
<p className="tracl-text">
{track.name}
</p>
</div>
)
}) : null}
</div>
)
}
You can add if if you make that a functional component and add if statement inside that.
ShowTopTracks = () =>{
if(this.props.toptracks){
return toptracks.map((toptracks,k) => {
const trackImg =toptracks.track[0].image[0]['#text'];
return (
<div
key = {k}
className ="track"
>
<img
src={trackImg}
className="trackImg"
alt="track"
/>
<p className="tracl-text">
{track.name}
</p>
</div>
)
})
}
else return null;
}
render() {
console.log('galler props',this.props);
const { toptracks } = this.props;
return (
<div>
{this.ShowTopTracks()}
</div>
)
}
As has been mentioned, toptracks by itself is undefined, declare it at the start of the render function as
const { toptracks } = this.props;
then you can use it without referencing this.props.toptracks
Secondly the map function takes arguments of the element and the index but you have declared them as toptracks again which creates aliasing issues (at least in my mind) and is also just plain confusing...
return toptracks.map((toptracks,k) =>
should ideally be
return toptracks.map((toptrack,k) => - then remember to update the toptracks inside the map callback to toptrack to make it clearer which is the collection and which is the element
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map for more info on the map function.
As already pointed out, if toptracks is not an array then you will need to turn it into an array or use .keys for example if it is an object.

Resources