Send value to child component in react - javascript

I have a component that takes a parameter that can be true or false, I put it in the console to check.
console.log(isContract);
//can be true ou false
I need to send this value through a form that will render another component.
This is the parent component:
return (
<Contract
savingsFactors={formValues.savingsFactors}
onFieldSubmit={...}
/>
)
And here in the internal component, if my value that came from the other component is true, I need to change the items
const Contract = ({ savingsFactors }) => (
<PutField
label={label}
placeholder={placeholder}
onBlur={...}
// if isContract === true, return this:
items={savingsFactors === 'true' ? FORM_VALUES : FORM_VALUES_NORMAL}
// if isContract === false, return this:
items={savingsFactors === 'true' ? ANOTHER_FORM_VALUES : ANOTHER_FORM_VALUES_NORMAL}
/>
);
What is the simplest way to send the isContract to the internal component and load the items according to the result?
I'm studying react and I'm having a lot of trouble with it, thanks a lot to those who help

I'm more familiar with defined props in React, but If I remember correctly, this should go something like this:
Edit 1
Added a render method inside the Contract component.
Edit 2
I just realized that you have a component inside your contract component XD which should be inside the render method itself. I think that might be the issue.
return (
<Contract
savingsFactors={formValues.savingsFactors}
isContract={isContract}
onFieldSubmit={...}
/>
)
And your component should be something like (After Edit 2):
const Contract = (props) => (
render(){
let items = [];
if (props.isContract === true)
{
items={props.savingsFactors === 'true' ? FORM_VALUES : FORM_VALUES_NORMAL}
}
//BTW, should this be !== true? :P
if (props.isContract === true)
{
items={props.savingsFactors === 'true' ? ANOTHER_FORM_VALUES : ANOTHER_FORM_VALUES_NORMAL}
}
return (
<div>
<h2>I have {items.length} items... or something...<h2>
<PutField
label={label}
placeholder={placeholder}
**items={items}** /*Maybe?*/
onBlur={...}
/>
</div>
)
}
);

Related

Render a component in jsx if a certain condition is met

Really new to react, I have this part of navigation in my Navbar.js using react-router-dom useLocation hook, I am able to get the active path I have to views and I want to render custom text when a user arrives at the view with JSX something like
if(path==="login" || path==="signin"){
`<h1>welcome</h1> ${userName}!`
}
How can I check if condition A or B is met then render the appropriate text using jsx
You must explicitly tell what to return, in your example you do not return anything. So, You can do either :
if (path === "login" || path === "signin") {
return (
<>
<h1>welcome</h1> {userName}
</>
);
} else {
return null;
}
Or use operator && to conditionally render.
{
(path === "login" || path === "signin") && (
<>
<h1>welcome</h1> {username}
</>
);
}
Read more about conditional rendering [here]
You need to:
Return the value
Use JSX and not a string
Put everything in a container such as a React Fragment
Such:
if(path==="login" || path==="signin") {
return <>
<h1>Welcome</h1>
{userName}!
</>;
}
… but that logic should probably be handled by your routes rather than an if statement.
You can use the ternary operator for conditional rendering in JSX like
{path==="login" || path==="signin" ? <> <h1>welcome</h1> {userName} </> : null}
Simple :-)

How to use ternary condition to render jsx using react?

i want to return jsx if some condition is true if not undefined should be returned.
below is my code,
const showInfo = (item) {
return (
<div>
<div>
<span>name</span>
</div>
<div>
<button>click</button>
</div>
</div>
);
}
const Parent = () => {
return (
<Child
onDone = {({item}) => {
notify ({
actions: (condition === 'value1' || condition === 'value2' ) &&
showInfo(item) //should put this condition into showInfo method
})
}}
/>
);
}
what i am trying to do?
the above code works. but now i want to put the condition inside the showInfo method. so if condition is true return jsx and if condition is false should return undefined.
what i have tried?
I have tried something like below
const showInfo = (item) {
return
{(condition === 'value1' || condition === 'value2' ) ? <div>
<div>
<span>name</span>
</div>
<div>
<button>click</button>
</div>
</div>
: undefined
}
);
}
const Parent = () => {
return (
<Child
onDone = {({item}) => {
notify ({
actions: showInfo(item) //error here
})
}}
/>
);
}
but the above tried code, gives error "Type 'void' is not assignable to type 'ReactNode'" at actions statement.
could someone help me with this. i am not sure if i have used ternary operator properly. thanks.
EDIT
after trying one of the answers provided,
notify is a method that is returned from usehook
and it evaluates to the component below
const Something: React.FC<SomethingProps> = ({
description,
actions,
...props
}) =>
(
<Header>
<Title>{title}</Title>
</Header>
{(description ||actions) && (
<Body> //this is displayed
{description && <Description>{description}</Description>}
{actions && <Actions>{actions}</Actions>}
</Body>
)}
);
here the component is displayed when the condition fails in showInfo component.
in showInfo i am returning undefined if condition fails but still in the Something component the is displayed even though i have {description || actions}
i am not sure what is happening here.what is the condition i have to check for actions to not display in this case
i have tried
{(description ||actions !== 'false') && (
<Body> //this is displayed
{description && <Description>{description}</Description>}
{actions && <Actions>{actions}</Actions>}
</Body>
)}
and this works. i am wondering why i should specifically mention
actions !== 'false'
instead of actions only
could someone help me with this. thanks.
If you want to return jsx from function you should wrap them inside some component. In this case you cen use <React.Fragment> or just <>. Another problem which I can see is that you probably forgot about arrow in you arrow function. Also don't know from where variable names condition comes from.
const showInfo = (item) => {
return (
<>
{ condition === "value1" || condition === "value2" ? (
<div>
<div>
<span>name</span>
</div>
<div>
<button>click</button>
</div>
</div>
) : undefined}
</>
);
};
Wouldn't it be better to use the useState or useEffect hooks?

React: Removing dynamic input from state, rerender

I am having trouble debugging this situation. I am attempting to remove an index from an array provided by an API. I store this in the Parent state after checking for prop change by it's parent. All is good here. I can store any prop changes in the Parent Form state.
The Parent Form maps everything in templateData and I determine what type it is and then render a component depending.
The remove button will always only be in a DynamicInputGroup so I am passing that function down via props. In the DynamicInputGroup I need to map through the array passed from the parent, then map the objects within that array to display each individual input. Again using props passed down to determine which input to render, same as parent.
Here is where things go wrong. When I click remove, let's say index 2 out of 5, I see that index being removed in the state, but what's rendered is index 5 being removed. I am unsure how to go about this. I've done research and read that keys come into play? I've tried to set a variable in the render function to the state so it'll refresh, but nothing. the last index always gets removed. Halp!
Parent Form
this.state = {
templateData: []
}
removeGroupItem = (index, inputName ) => {
let group = `${inputName}__group`
const newState = this.state;
if (index === -1) return;
newState.templateData[group].splice(index, 1);
console.log(newState) // THIS SHOWS CORRECT STATE
this.setState(newState);
}
render() {
return (
{Object.keys(this.state.templateData).map((name, key) => {
let data = { // SETTING inputType TO DISPLAY CORRECT COMPONENT }
return (
<Fragment key={key}>
{data.inputType == 'input' && <DynamicTextInput {...data} />}
{data.inputType == 'rtf' && <DynamicRTF {...data} />}
{data.inputType == 'img' && <DynamicImageUpload {...data} />}
{data.inputType == 'group' && <DynamicInputGroup {...data} removeGroupItem={this.removeGroupItem} />} // COMPONENT THAT HOLDS REMOVE BUTTON
</Fragment>
)
})}
)
}
DynamicInputGroup Componenet
this.state = {
value: []
}
componentDidMount() {
this.setState({ value: this.props.value })
}
componentDidUpdate(prevProps) {
if (this.props.value !== prevProps.value) {
this.setState({
value: this.props.value
})
}
}
render() {
return (
<Fragment>
<Typography>{this.props.inputName}</Typography>
{this.state.value.map((value, key) => {
return (
<span>
{Object.keys(value).map((input, index) => {
let data = { // SETTING inputType TO DISPLAY CORRECT COMPONENT }
return (
<Grid key={index}>
{data.inputType == 'input' && <DynamicTextInput {...data} />}
{data.inputType == 'rtf' && <DynamicRTF {...data} />}
{data.inputType == 'img' && <DynamicImageUpload {...data} />}
{data.inputType == 'group' && <DynamicInputGroup {...data} />}
</Grid>
)
})}
{key >= 1 ? <span onClick={() => this.props.removeGroupItem(key, this.props.inputName)}>remove</span> : ''}
</span>
)
})}
</Fragment>
)
}
Then, if needed a DynamicTextInput
const DynamicTextInput = (props) => {
return (
<Grid>
<TextField
name={props.inputName} label={props.inputName} defaultValue={props.value}
size="small" variant="outlined" fullWidth multiline
/>
</Grid>
)
}
Using index as component's key is a bad practise, especially if it is a list that is changing. Key should be unique identifier of component.
If you don't have unique identifier for your controls, you could create one based on the timestamp, some sort of uuid, that you will store in your state and that will always and always just reference the one control element.
Imagine that you have situation where you have 1 input element that has value of TextInput 1. Then you add another input in from of it and it inherits its key. Now you have broken shadow DOM since you are referencing to new input instead of the old one because input with key={0} has value of TextInput 1, but that's not what you expect, because you want to refer to another input.
This line has the problem:
<Fragment key={key}>
Since you are using the index of the array to render those Fragments, the reconciliation fails length times and the "last element" gets removed. I would really recommend watching this video, so helpful. You should use an unique identifier.

React - How to set className conditionally from a json file

I have the following piece of code for my component. The desired behaviour for the button is to change the className for each li, but this is not working.
const Booking = (props) => {
let { hidden } = useContext(ContextBooking)
let completed = props.completed
return (
<li
className={ //should change according to the button click below
completed && hidden ?
'booking-complete hide'
: completed ?
'booking-complete'
:
'bookings'
}}
key={props.id}
id={props.id}
>
<h3>{props.date}</h3>
<h4>{props.time}</h4>
<h5>{props.name}</h5>
</li>
)
}
{!completed && (
<button
onClick={() => {
if (!completed && !hidden) {
completed = !completed //does make it false
hidden = !hidden //does make it false
} //above works, but won't change classname for each 'li'
else if (completed && hidden) {
completed = !completed
hidden = !hidden
}
}}>
Complete
</button>
)}
In another component, I am creating multiple of these 'Booking' components, by filling in the details with info that come from a json file
const DisplayBookings = () => {
const display = (day) => allBookings.map(item => //allBookings is a json file
item.day === day &&
<Booking
completed={item.completed}
key={item.id}
id={item.id}
time={item.time}
name={item.name}
date={item.date}
/>
)
I emphasised json file as I believe it could be the source of the problem?
A component can in most cases not update its own props, and doing so even if possible is an antipattern.
You can instead use state for updating the components state.
You can create hooks for setting state like this:
const [isCompleted, setIsCompleted] = useState(props.completed);
const [isHidden, setIsHidden] = useState(hidden);
Then in your onClick you use this to update the values:
setIsCompleted(!isCompleted);
setIsHidden(!isHidden);

How to handle a generated big form in React Js

I trying to generate a big form based on what I get from the server.
sometimes I generate 32 elements sometimes 57 or 4 I don't know.
I try to create a component for each type of element like select, text, number, textarea and so on.
each component passes the value to the parent component and setState the value to the parent.
imagine I have 20 inputs and custom select-option elements.
when I type something in one of the inputs characters show up after 2seconeds and there is a huge lag in my component.
I know because of the setState method my hole component (I mean my parent component or my single source of truth) re-renders and causes the problem.
in fact, I don't know other ways.
I try to use a "this.VARIABLE" and instead of setState, I update the "this.VARIABLE" and problem solved. but I need my state.
any help or solution?
my code (parent Component, source of truth ):
// ---> find my component based on the type that I get from server
findComponent ( item , index) {
if ( item.type === 'text' || item.type === 'number') {
return (<Text data={item} getUpdated={this.fetchingComponentData} />);
} else if ( item.type === 'longtext') {
return (<Textarea data={item} getUpdated={this.fetchingComponentData} />);
} else if ( item.type === 'select' ) {
return (<SelectOption data={item} getUpdated={this.fetchingComponentData} />);
} else if ( item.type === 'autocomplete') {
return (<AutoTag data={item} url={URL1} getUpdated={this.fetchingComponentData} />);
} else if ( item.type === 'checkbox_comment' ) {
return (<CheckboxComment data={item} getUpdated={this.fetchingComponentData} />);
} else if ( item.type === 'multiselect' ) {
return (<Multiselect data={item} getUpdated={this.fetchingComponentData} />);
} else {
return (<p>THERE IS NO TYPE OF => {item.type}</p>);
}
}
// ----> if i setState here ==> big lag
fetchingComponentData(OBJ) {
let index = null;
// let Answer = [...this.state.Answer];
index = Helper.find_item(this.Answer , OBJ , 'unique_key');
if ( index === -1 ) {
this.Answer.push(OBJ);
} else {
this.Answer[index].value = OBJ.value;
}
}
// ----> in my render method
render () {
return (
<React.Fragment>
<div className="row Technical section" data-info="Technical">
<div className="col-6">
{data.map( (item,index) => {
return (
<React.Fragment key={index}>
<div className="rowi">
{item.attributes.map( (item, index)=> {
return <React.Fragment key={index}>{this.findComponent(item, index)}</React.Fragment>;
})}
</div>
</React.Fragment>
)
})}
</div>
<div className="col-6"></div>
</div>
</React.Fragment>
);
}
Have you tried to make an object out of your components and pass it to setState at once?
const nextState = componentList.map(component => {
return {[component]: value};
});
this.setState({...nextState});
Edit: Okay i got another part you could do better.
You should build an array with you components in componentWillMount function instead of fetching all the data inside the render. Like you said, it's updating everytime any state changes, and all the components are also updating with the parent.
This is to be made in addition with what I suggested before, but it is of far more importance because of the impact on the ressource.

Categories

Resources