I know this might have some similar questions but I don't seem to able to find the solution for my situation.
I have a form that will be submitted with the content of the child component, the child component is appended onClick and can be appended infinitely. How can I get the value from all the child component, and to post it.
This is index.js
class ListOfProducts extends React.Component {
constructor()
{
super();
this.appendChild = this.appendChild.bind(this);
this.state = {
children: [],
}
}
appendChild() {
this.setState({
children: [
...this.state.children, <NewComponent/>
]
});
}
render() {
return (
<form>
<div>
<pre><h2 className="h2"> Sales Order</h2></pre>
<div className="box" style={{height: '520px', width: '1300px', position: 'relative', overflow: 'auto', padding: '0'}}>
<div style={{height: '1000px', width: '1000px', padding: '10px'}}>
<div>
{this.state.children.map(child => child )}
</div>
</div>
</div>
<button className="addbut" onClick={() => this.appendChild()}>Add Items</button>
</div>
</form>
)
}
}
This is partial code of NewComponent.JS
<select
name="sel"
className="sel"
value={this.state.selecteditems}
onChange={(e) =>
this.setState({selecteditems: e.target.value})}
>
{this.state.data.map(item =>
<option key={item.productID} value={item.unitPrice}>
{item.itemName}
</option>
)}
</select>
{/*unit price*/}
<p>Unit Price: RM {this.state.selecteditems} </p>
{this.state.selecteditems.length ? (
<p>Quantity: </p>
) : null }
{/*button to add quantity*/}
{this.state.selecteditems.length ? (
<button onClick={this.addPro}> + </button>
) : null }
{/*textbox for quantity*/}
{this.state.selecteditems.length ? (
<input type="text" ref="quan" placeholder="Quantity"
value={this.state.quantity}
onChange={(e) =>
this.setState({quantity: e.target.value})}
>
</input>
) : null }
{/*button to decrease quantity}*/}
{this.state.selecteditems.length ? (
<button onClick={this.decPro}> - </button>
) : null }
{/*subtotal*/}
{this.state.selecteditems.length ? (
<p>Sub Total: RM {this.state.subtot} </p>
) : null }
Thanks in advance!
Quick and dumb: add callback like this
<NewComponent onChange={this.onNewComponentChange}/>
(and implement calling of this onChange callback at every change at NewComponent of course)
There are two ways I can think of getting the value from the child component -
Have a state management system (something like redux) which can actually store the data of all the child components. As and when the child component's data changes, it should be synced to the store and the same can be used by the parent to post the data on submit.
Assign ref to each of the child component when it gets appended. save those ref values in a array. Iterate through those references on submit of the form and call some specific getter function of child component to give you its data.
Preferred way is the first method.
Related
Don't get this confused with checking each radio button I have on the page. I want to implement a check all button that sets the value of a nested object state equal to a certain value. I am storing each question in a nested state. Ex.
formQuestions({
kitchen: [question,question2,question3],
living: [question,question2,question3]
})
Four radio buttons are being made for each question. Now one radio button can only be selected at once. Each radio button has its' own value. Ex. `"Good", "Fair", "Poor", "N/A".
When a radio button is selected a state is generated dynamically for that section and question. Ex.
formAnswers({
kitchen: {
question: "Good"
question2: "Poor"
}
})
The goal here is the button that I want to create that checks only one value for each question Ex. clicks button question: "Good", question2: "Good" etc..
For me to set the state of a dynamic value I would need the "Section name" lets call it Name and the "Question" we'll call it question. That would give me access to the value like so formAnswers[Name][question]: value
I am trying to set that state from a component called SectionHeader. These contain the buttons.
SectionHeader.js
import { FormAnswersContext, FormQuestionsContext } from "../../Store";
function SectionHeader({ title, name }) {
const [formAnswers, setFormAnswers] = useContext(FormAnswersContext);
const [formQuestions, setFormQuestions] = useContext(FormQuestionsContext);
return (
<div>
<h1 className={styles["Header"]}>{title}</h1>
<div className={styles["MarkAllWrapper"]}>
<button className={styles["MarkAll"]}>
Mark all items as "Good" in this section
</button>
<br />
<button className={styles["MarkAll"]}>
Mark all items as "N/A" in this section
</button>
</div>
</div>
);
}
The parent of Section Header and the rest of the form code excluding the child radio buttons which I have explained, are in another component LivingRoom.js
LivingRoom.js
import { FormQuestionsContext, FormAnswersContext } from "../../Store";
function LivingRoomForm({ Name }) {
const [expanded, setExpanded] = useState(false);
const [formQuestions, setFormQuestions] = useContext(FormQuestionsContext);
const [formAnswers, setFormAnswers] = useContext(FormAnswersContext);
const array = formQuestions.living;
const onChange = (e, name) => {
const { value } = e.target;
setFormAnswers((state) => ({
...state,
[Name]: { ...state[Name], [name]: value },
}));
};
const handleOpen = () => {
setExpanded(!expanded);
};
return (
<div>
<Button
className={styles["CollapseBtn"]}
onClick={handleOpen}
style={{ marginBottom: "1rem", width: "100%" }}
>
<p>LIVING ROOM INSPECTION</p>
<FontAwesome
className="super-crazy-colors"
name="angle-up"
rotate={expanded ? null : 180}
size="lg"
style={{
marginTop: "5px",
textShadow: "0 1px 0 rgba(0, 0, 0, 0.1)",
}}
/>
</Button>
<Collapse className={styles["Collapse"]} isOpen={expanded}>
<Card>
<CardBody>
{array ? (
<div>
<SectionHeader title="Living Room Inspection" name={Name} />
<div
className={styles["LivingRoomFormWrapper"]}
id="living-room-form"
>
{array.map((question, index) => {
const selected =
formAnswers[Name] && formAnswers[Name][question]
? formAnswers[Name][question]
: "";
return (
<div className={styles["CheckboxWrapper"]} key={index}>
<h5>{question}</h5>
<Ratings
section={Name}
question={question}
onChange={onChange}
selected={selected}
/>
</div>
);
})}
</div>
<br />
<ImageUploader name="living" title={"Living Room"} />
</div>
) : (
<div></div>
)}
</CardBody>
</Card>
</Collapse>
</div>
);
}
If there is anything I am missing please let me know, I would be happy to share it. Cheers
Edit: for anyone that needs the radio buttons component.
Ratings.js
import React from "react";
import { FormGroup, CustomInput } from "reactstrap";
function Ratings({ selected, section, question, onChange }) {
return (
<div>
<FormGroup>
<div>
<CustomInput
checked={selected === "Good"}
onChange={(e) => onChange(e, question)}
type="radio"
id={`${section}_${question}_Good`}
value="Good"
label="Good"
/>
<CustomInput
checked={selected === "Fair"}
onChange={(e) => onChange(e, question)}
type="radio"
id={`${section}_${question}_Fair`}
value="Fair"
label="Fair"
/>
<CustomInput
checked={selected === "Poor"}
onChange={(e) => onChange(e, question)}
type="radio"
id={`${section}_${question}_Poor`}
value="Poor"
label="Poor"
/>
<CustomInput
checked={selected === "N/A"}
onChange={(e) => onChange(e, question)}
type="radio"
id={`${section}_${question}_NA`}
value="N/A"
label="N/A"
/>
</div>
</FormGroup>
</div>
);
}
I do not completely understand your question, I am sorry but I think this will help you.
Here is an implementation of radio buttons using react -
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
handleChange = e => {
const { name, value } = e.target;
this.setState({
[name]: value
});
};
render() {
return (
<div className="radio-buttons">
Windows
<input
id="windows"
value="windows"
name="platform"
type="radio"
onChange={this.handleChange}
/>
Mac
<input
id="mac"
value="mac"
name="platform"
type="radio"
onChange={this.handleChange}
/>
Linux
<input
id="linux"
value="linux"
name="platform"
type="radio"
onChange={this.handleChange}
/>
</div>
);
}
}
After a few attempts, I was able to figure out the solution to this issue.
The key here was to figure out a way to get gather each question so that it may be used as a key when setting the state. As my questions were stored in a ContextAPI, I was able to pull them out like so...
this may not be the best solution however it worked for me.
const setStateGood = () => {
formQuestions[name].map((question) => {
setFormAnswers((state) => ({
...state,
[name]: { ...state[name], [question]: "Good" },
}));
});
};
const setStateNA = () => {
formQuestions[name].map((question) => {
setFormAnswers((state) => ({
...state,
[name]: { ...state[name], [question]: "N/A" },
}));
});
};
I was able to map through each question since the name is being passed through props is a key inside the actual object, formQuestions[name]. Because i'm mapping through each one I can set that question as a key and return the new state for each question to whatever I would like.
However, if I was to create an onClick={setState('Good')}, React didn't like that and it created an infinite loop. I will look for more solutions and update this post if I find one.
I would like to append div into the current div, having multiple divs but each with their unique items in the dropdownlist.
Based on other answers and tutorials I have managed to come up with this, but the duplicated div has the same value as the original div, and the value changes follows as the original div changes.
Is there any way for me to make it not having the same value as the one in above? I would also like to make it sortable in the future
Thanks in advance!
Sandbox for full code
https://codesandbox.io/s/stoic-mestorf-k997d
Code for render section
render() {
return (
<div>
<h2>Sales Order</h2>
{this.state.items.map((item, index) => (
<span key={index}>
<div>
{/*dropdownlist for available items*/}
<select
name="sel"
value={this.state.selecteditems}
onChange={this.handleSelect(index)}
>
{this.state.data.map(item =>
<option key={item.productID} value={item.unitPrice}>
{item.itemName}
</option>
)}
</select>
{/*unit price*/}
<p>Unit Price: RM {this.state.selecteditems} </p>
{/*button to add quantity*/}
{this.state.selecteditems.length ? (
<button onClick={this.addPro}> + </button>
) : null }
{/*textbox for quantity*/}
{this.state.selecteditems.length ? (
<input type="text" ref="quan" placeholder="Quantity"
value={this.state.quantity}
onChange={(e) =>
this.setState({quantity: e.target.value})}
>
</input>
) : null }
{/*button to decrease quantity}*/}
{this.state.selecteditems.length ? (
<button onClick={this.decPro}> - </button>
) : null }
{/*subtotal*/}
{this.state.selecteditems.length ? (
<p>Sub Total: RM {this.state.subtot} </p>
) : null }
{/*button to remove all ements*/}
<button onClick={this.handleDelete(index)}>X</button>
</div>
<br/><br/>
</span>
))}
<button onClick={this.addElements} >Add</button>
</div>
)
}
Code for appending the div
addElements = e => {
e.preventDefault()
let items = this.state.items.concat([''])
this.setState({
items,
})
}
Sorry for the poor choices of word in my question, what I basically want is just to add another dropdown list with the default value from the same list of data from the API.
So what I did was basically created a new component and appended it into the current component
import {NewComponent} from './components/NewComponent';
class App extends React.Component {
constructor()
{
super();
this.state = {
children: []
};
}
appendChild() {
this.setState({
children: [
...this.state.children, <NewComponent/>
]
});
}
render() {
return (
<div>
<button onClick={() => this.appendChild()}>Append Child</button>
<div>
{this.state.children.map(child => child)}
</div>
</div>
Thanks to everyone who tried to help me, hope this can help anyone who come across this in the future.
I have a save button that saves the checkbox and the number field, then makes a call to update those numbers. I am adding a 'Cancel' button that I want to be able to use to revert the status to the previous state (prior to saving). what is the best way to do this in React?
This is my code
class Alerts extends Component {
state = {
prevId: null,
checked: this.props.preferences.last_order_alert_enabled,
days: this.props.preferences.last_order_alert
};
static getDerivedStateFromProps(props, state) {
// Store prevId in state so we can compare when props change.
// Clear out previously-loaded data (so we don't render stale stuff).
if (props.dealerID !== state.prevId) {
//update action goes here...
//props.actions.getUsers(props.dealerID);
return {
prevId: props.dealerID
};
}
// No state update necessary
return null;
}
componentDidMount = () => {
console.log('mountDays',this.props.preferences.last_order_alert);
console.log('mountCheck',this.props.preferences.last_order_alert_enabled);
this.setState({ days: this.props.preferences.last_order_alert });
this.setState( {checked: this.props.preferences.last_order_alert_enabled})
};
toggleAlerts = e => {
console.log('lastOrderEnable', this.props.preferences.last_order_alert_enabled);
console.log('lastOrderDaysAlert', this.props.preferences.last_order_alert);
this.props.actions.updateLastOrderAlert(
this.props.preferences.id,
this.props.dealerID,
this.props.isGroup,
this.props.preferences.last_order_alert = this.state.checked,
this.props.preferences.last_order_alert_enabled = this.state.days
);
};
handleCheck = event => {
console.log('called', {checked: Number(event.target.checked) });
this.setState({checked: Number(event.target.checked) })
};
handleChange = e => {
console.log('days', {days: e.target.value});
this.setState({days: e.target.value})
};
render = () => {
return (
<div className="preferenceContainer">
<div className="preferenceRow lg">
<div className="preferenceLabelWrapper">
<div className="preferenceLabel">Enable Alert</div>
<div className="preferenceSubLabel">
Toggles the Last Order Alert Email
</div>
</div>
<div className="preferenceInput">
<input
type="checkbox"
checked={this.state.checked}
onChange={this.handleCheck.bind(this)}
/>
</div>
</div>
<div className="preferenceRow">
<div className="preferenceLabelWrapper">
<div className="preferenceLabel">Days Since Last Order</div>
</div>
<div className="preferenceInput">
<input
type="number"
value={this.state.days}
// onBlur={this.handleAlertDays}
onChange={this.handleChange.bind(this)}
style={{ width: "50px" }}
/>
</div>
</div>
<div className="preferenceRow" style={{ display: "flex" }}>
<button
className={'btn btn-default'}
type="submit"
onClick={this.componentDidMount}
>Cancel
</button>
<button
style={{ marginLeft: "auto" }}
className={'btn btn-primary'}
type="submit"
onClick={this.toggleAlerts}
>Save
</button>
</div>
</div>
);
};
}
Right now is currently saving the changes and rendering them back when I hit the cancel button(prior to hitting save). However, after hitting save and going to another page and then coming back, it does not render with the initial state in componentDidMount.
Never use lifecycle methods in onClick
If I understood correctly, you should use componentDidUpdate(prevProps, prevState) lifecycle method to get previous props and previous state after change them. Every time after redrawing react will give you previous props and previous state in that method.
Sorry, if this is not what you need
I am working on a project and i want to display a hidden <div> below another <div> element using an event handler but when i click the icon that is meant to display the div, the whole page becomes blank
This is image I want:
This is what i get
I have tried to check through the internet for some places where i could get the solution. Well i found something similar to what i had done but the error still happens for me.
class PostItTeaser extends Component {
state = {
postIt: false,
moreIt: false,
}
togglePostIt = e => {
e ? e.preventDefault() : null
this.setState({ postIt: !this.state.postIt })
}
_toggle = e => {
e ? e.preventDefault() : null
this.setState({
moreIt: !this.state.moreIt,
})
}
Child = () => <div className="modal">Hello, World!</div>
render() {
let { postIt } = this.state
let { moreIt } = this.state
let {
type,
group,
disabled,
session: { id, username },
} = this.props
return (
<div>
<div
className="post_it inst"
style={{ marginBottom: type == 'group' && 10 }}
>
<img src={`/users/${id}/avatar.jpg`} alt="Your avatar" />
<div className="post_teaser">
<span
className="p_whats_new"
onClick={disabled ? null : this.togglePostIt}
>
What's new with you, #{username}? #cool
</span>
<span className="m_m_exp" data-tip="More" onClick={this._toggle}>
<MaterialIcon icon="expand_more" />
</span>
</div>
</div>
{moreIt && <Child />}
{postIt && (
<PostIt back={this.togglePostIt} type={type} group={group} />
)}
</div>
)
}
}
From skimming through the code I believe you need to bind the scope, since the function you're calling is using this.setState, it needs this to be the react component, not the event you're listening to:
onClick={this._toggle.bind(this)}
You can also bind the functions scope in the constructor. Or, a less memory performant & ugly way:
onClick={() => { this._toggle(); } }
I made a stateless component with an internal variable to reference an input, as below. This is working fine.
const MyStatelessComp = ({ team, teamProgress, onSet, editing, enableEdit }) => {
let input
return (
<div>
<div className="team__goal-target_header" >Team's Savings Target</div>
<div className="team__goal-target_value" >
M$
<input
ref={ el => input = el }
style={{width: '75px', border: 'none'}}
onChange={() => onSet({teamId: team.id, goalValue: parseInt(input.value, 10) || 0}) }
/>
<div
ref={ el => input }
style={{
display: !input || (!isNaN(parseFloat(input.value)) && isFinite(input.value)) ? 'none' : 'block'
}}
>Must be numeric</div>
</div>
</div>
)
}
I want to validate input and display a notification Must be numeric is the anything that cannot be converted to a number is entered into my input field. That is not working however. How do I make input in the context of the "warning div" reference the value of the input?
Realize that this is not an unorthodox way to working with stateless components, but it would save me lots of pain.
Thank you.
Why use a stateless component when he can be a simple statefull component ?
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: null
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
render() {
const isNumber = !isNaN(this.state.value);
return (
<div>
<div className="team__goal-target_header">Team's Savings Target</div>
<div className="team__goal-target_value">
M$
<input
style={{ width: "75px", border: "none" }}
onChange={this.handleChange}
value={this.state.value}
/>
{isNumber ? "" : <div>Must be numeric</div>}
</div>
</div>
);
}
}
You can also toggle the div content or create a new alert component and toggle this component.