Doing if logic inside a functional component calling another function - javascript

How can I do if and else logic inside a functional component. For example
const FunctionA = ({ nodes, onOpen}) => {
nodes.map((item, ind) => (
if(item.nodes.length===0) {
return (<div>{item.name}</div>)
}
else
return (
<FunctionB
name={item.name}
nodes={item.nodes}
/>
);
}
Not sure if that makes sense. And function B renders the nodes. And calls Function A again to create a nested sidenav which is based on directory structure. I want to check if it's a file and therefore has a property of empty nodes. Or if it's a directory and has further nesting. If has further nesting. Then call function B. Repeat. When I actually try what I put above. I get errors that nothing was returned.

You are not returning anything from FunctionA, and you cant use if/else statements inside an arrow function without a function body.
If you give the arrow function a body, wrap everything in a single topmost element e.g. React.Fragment and return it, it will work.
const FunctionA = ({ nodes, onOpen }) => {
return (
<React.Fragment>
{nodes.map((item, ind) => {
if (item.nodes.length === 0) {
return <div>{item.name}</div>;
} else {
return <FunctionB name={item.name} nodes={item.nodes} />;
}
})}
</React.Fragment>
);
};

Related

What gets passed where and why between various constructions (const, function, arrow functions, IIFEs) in JavaScript/React

I'm trying to understand this (radically simplified for purposes of asking this question) bit of code from Wordpress:
function MyFooContainer( {
attr1,
attr2,
attr3,
someVar
} ) {
// do some stuff
return (
<>
// some components
</>
);
}
const MyFooWrapper = withDispatch( (dispatch, ownProps, registry) => ( {
// a bunch of code doing stuff, but no returns
})
)( MyFooContainer );
const MyExportedFunction = (props) => {
const { someVar } = props;
const myTest = true;
const Component = myTest
? MyFooWrapper
: SomethingElse;
return <Component { ...props } />
};
export default MyExportedFunction;
The way I understand things (which is probably poorly, as I'm new to both JS and React), someone calls MyExportedFunction with a bunch of parameters. One of those gets explicitly pulled out into someVar, by name. All of them get passed to Component as individual arguments (because of the ellipsis expansion). Component because of the way the test is set up, is just MyFooWrapper, which takes three arguments in order, so ... the first three props had better map to dispatch, ownProps, and registry? After that, I was really confused, but I guess base on this question that
const myFunction = withDispatch((args) => { })(MyFooContainer);
is an IIFE and MyFooContaner is passed as an argument to the withDispatch? But where did MyFooContainer get its arguments?
While we're here, why is MyFooContainer explicitly defined as a function, whereas the others are assigned as const? And last but not least, where does its return go? Who is catching that?

Rerender only specific Child Components in JSX map function

I am mapping through an array, which returns JSX Components for each of the items in the array. During runtime I want to pass down values. If they match the value of the individual items, their individual component gets modified.
I am trying to find a way to achieve this without rerendering all components, which currently happens because the props change
I have tried using shouldComponentUpdate in a class component, but it seems this way I can only compare prevState and prevProps with the corresponding changes. I have further considered useMemo in the Map function, which didnt work, because it was nested inside the map function.
const toParent=[1,2,4,5]
Parent Component:
function parent({ toParent }) {
const [myNumbers] = useState([1,2,3,4, ..., 1000]);
return (
<div>
{myNumbers.map((number, index) => (
<Child toChild = { toParent } number = { number }
index= { index } key = { number }/>
))}
</div>
)
}
Child Component:
function Child({toChild, number, index}){
const [result, setResult] = useState(() => { return number*index }
useEffect(()=> {
if (toChild.includes(number)) {
let offset = 10
setResult((prev)=> { return { prev+offset }})
}
}, [toChild])
return (
<div style={{width: result}}> Generic Div </div> )
}
The solution to my problem was using the React.memo HOC and comparing the properties to one another and exporting it as React.memo(Child, propsAreEqual).
Performance
This way other methods like findElementbyId (not recommended in any case) and shouldComponentUpdate to target specific items in a map function can be avoided.
Performance is quite good, too. Using this method cut down the rendering time from 40ms every 250ms to about 2 ms.
Implementation
In Child Component:
function Child(){...}
function propsAreEqual(prev, next) {
//returning false will update component, note here that nextKey.number never changes.
//It is only constantly passed by props
return !next.toChild.includes(next.number)
}
export default React.memo(Child, propsAreEqual);
or alternatively, if other statements should be checked as well:
function Child(){...}
function propsAreEqual(prev, next) {
if (next.toChild.includes(next.number)) { return false }
else if ( next.anotherProperty === next.someStaticProperty ) { return false }
else { return true }
}
export default React.memo(Key, propsAreEqual);

How to pass ref from parent element to children

I am facing a problem which causes a lot of headaches and I just can't solve it.
I have a component which is used through the whole application (about 100 times in total) and the main goal of it to render some list based on children (children is a function which returns the child component) and payload (payload is an array of objects, which holds information about future children).
<GenericList
children={this.renderElement}
payload={this.state.treeData}
// other props...
/>
Here is the renderElement
renderElement = (role) => {
return (
<BaseListItem>
<div className="fb jCenter">
<Text
title={role.Name}
>
{role.Name}
</Text>
</div>
</BaseListItem>
);
};
renderElement can both return RFC or RCC.
Inside GenericList I should somehow manage it to pass ref to RFC or RCC.
The Part of code, which implements that logic is this
cloneChildrenElement({ payload, children, selected, payloadSortBy }) {
if (payload && payload.length) {
const { selectBy, scrollToIndex, idToScrollIndex } = this.props;
const data = sortPayload(payload, payloadSortBy);
return data.map((item, index) => {
return cloneElement(children(item), {
item,
isActive,
key: index,
onDoubleClick: this.handleDblClick,
ref: (node) => {
console.log(node, 'node');
if (Number.isInteger(scrollToIndex) || isActive) {
this.genericContentSelected = node;
}
},
});
});
}
return false;
}
I have also tried to implement ref forwarding, but in that case, I should manually do it for all components that use that logic.
I want to know whether it's possible to achieve this by only making changes inside GenericList component.

How to map list and implement async within it?

renderDuration(x) {
return 'abc'
}
render() {
return (
<View>
{this.props.list.map(x => (
<Text>{this.renderDuration(x)}</Text>
))}
</View>
)
}
The above code is working perfectly fine. The situation is very basic which is looping the list and for each of the element, call the method renderDuration and get the individual string value. Now take a look below.
async renderDuration(x) {
let someAsyncOpt = await getTextFromSomewhere();
return someAsyncOpt;
}
So once the same method we change it to async method, it breaks and hitting exception
Invariant Violation: Invariant Violation: Objects are not valid as a React child (found: object with keys {_40, _65, _55, _72}). If you meant to render a collection of children, use an array instead.
I understand that a viable option is to get whatever data that's needed first, instead while render. This question is basically trying to explore the possibility to perform async operation while mapping, if it make sense?
UPDATES:
I've included the below code to show that it has nothing to do with wrong type of returning from async opt. It's basically the moment we include the keyword async, it will break
async renderDuration(x) {
return 'abc';
}
(Update) try to use this:
class Alpha{
// ...
// Update :
async renderDuration(x) {
let someAsyncOpt = await Promise.all(getTextFromSomewhere());
return someAsyncOpt;
}
render() {
return (
// Old :
<View>
{this.props.list.map(x => (
<Text>{this.renderDuration(x)}</Text>
))}
</View>
// Update :
<View>
{
this.props.list.map(
async (x) => { await this.renderDuration(x) }
);
}
</View>
)
}
}

How to pass a string as an argument and attach it to a REACT state

I want to pass a string as an argument that finishes this.state and builds out HTML. I create an arrow function below the render() and above the return
const createHtml = (name, stateProperty) => {
return (
<h1>{name}</h1>
<p>{this.state.stateProperty+'_x'}</p>
<p>{this.state.stateProperty+'_y'}</p>
)
}
createHtml('Jon', 'company')
// the result should be
//<h1>{Jon}</h1>
//<p>{this.state.company_x}
I've tried different variations and none worked or came up 'Cant read property'
<p>{this.state.${statePropety}+'x'}</p>
The contents of a {} block are regular Javascript expressions.
Therefore, you must write them as regular Javascript, not JSX.
You want this.state[stateProperty + 'x'].
this is a functional component so you have not a state , but props so :
const createHtml = ({name, Property}) => {
return (
<h1>{name}</h1>
<p>{Property+'_x'}</p>
<p>{Property+'_y'}</p>
)
}
and use it somewhere :
<createHtml name='Joe' Property = 'something' />

Categories

Resources