i just try to do something like that (when user select, continue to other component):
render() {
return(
<select onChange={(e)=> <CountrySelected countrySelected=
{e.target.value}/> }>
{this.state.countryArray.map( (e) => <option> {e.name} </option>) }
</select>
);
}
just if i can't do it, so pls continue reading the next code to tell me how can i solve the follow problem:
The following code work well for me:
class App extends Component {
state = {
countryArray: [],
selectedCountry: undefined
};
constructor(p) {
super(p);
// here i'm Initializing the this.state.countryArray
}
countrySelectionHandler=(countryName)=> {
this.setState({ selectedCountry:
this.state.countryArray.find(e=>e.name===countryName) });
}
render() {
return (
<div>
<CountryDropDown
countryArray={this.state.countryArray}
countrySelectionHandler={this.countrySelectionHandler} />
{
this.state.selectedCountry ?
// this component just display on the screen the selectedCountry name
<CountryName countryName={this.state.selectedCountry.name} />
:
<div> no country selected</div>
}
</div>
);
}
}
'CountryDropDown' component:
const countryDropDown = (p)=>(
<select onChange={(e)=> p.countrySelectionHandler(e.target.value)}>
{p.countryArray.map( (e) => <option> {e.name}
</option>) }
</select>
)
but now when user select, it's will rerender 'CountryDropDown' components for no reason.
so how can i tel react to rerender only this part:
{
this.state.selectedCountry ?
// this component just display the selectedCountry name
<CountryName countryName={this.state.selectedCountry.name} />
:
<div> no country selected</div>
}
so the way i try to solve it:
when user select, continue to other components(CountrySelected) and there render only 'CountryName' component
If you do not use PureComponent or custom shouldComponentUpdate lifecycle function.
Children component will be re-rendered when parent component re-render.
class CountryDropDown extends Component {
shouldComponentUpdate(nextProps) {
return (this.props.countryArray !== nextProps.countryArray)
}
render() {
const { countrySelectionHandler, countryArray } = this.props
return (
<select onChange={countrySelectionHandler}>
{countryArray.map((e) => <option> {e.name}
</option>)}
</select>
)
}
}
PureComponent
class CountryDropDown extends PureComponent {
render() {
const { countrySelectionHandler, countryArray } = this.props
return (
<select onChange={countrySelectionHandler}>
{countryArray.map((e) => <option> {e.name}
</option>)}
</select>
)
}
}
PureComponent has shallow compare shouldComponentUpdate function by default.
Related
Relatively new to React here, so forgive me if this is an easy fix.
I have a component that has a form - this form has 4 choices, and my intention is that every time you click an option on the form, a child component is rendered with data from the choice selected.
Here is the issue I am having. Currently, I'm able to load the child component upon my first choice selection, but I cannot reload the component unless I select the default value first.
Attached is my code: App.js; here is the codesandbox: https://codesandbox.io/s/blissful-agnesi-1wo7s
import React, { Component } from 'react';
import ChildComponent from './ChildComponent';
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: '',
prevValue: '',
submitted: false
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({prevValue: this.state.value});
this.setState({value: event.target.value});
this.setState({submitted: true});
event.preventDefault();
}
render() {
var renderingBool = false;
if (this.state.submitted) {
if (this.state.value !== "--") {
renderingBool = true;
}
}
return (
<div className="Base">
<h1> By Productline </h1>
<form>
<label className="label">
Select Workflow: {" "}
<select value={this.state.value} onChange={this.handleChange}>
<option value="--"> -- </option>
<option value="test1">test1</option>
<option value="test2">test2</option>
<option value="test3">test3</option>
</select>
</label>
</form>
<div>
{renderingBool && <ChildComponent history={this.props.history} detail={this.state.value}/>}
</div>
</div>
);
}
}
export default App;
ChildComponent.js
import React, {Component} from 'react';
class ChildComponent extends Component{
constructor(props) {
super(props);
this.input = props.detail;
}
render() {
return(
<h1>{this.input}</h1>
);
}
}
export default ChildComponent;
I currently have this set up because if I do not have a boolean before calling the child component, I will not be able to render it. So, it makes sense that I have to set renderingBool to false (by selecting "--") and then set it to true again by selecting another choice, all in an effort to re-render the childComponent.
Is there a workaround to this? Can I re-render the child component without having to select the default value?
Issue
Child component isn't reacting to updated props. You only see the updated value after switching the select value back to "--" and the component is remounted.
{renderingBool && ( // <-- when false, unmounts child
<ChildComponent
history={this.props.history}
detail={this.state.value}
/>
)}
class ChildComponent extends Component{
constructor(props) {
super(props);
this.input = props.detail; // <-- only gets prop value when mounting
}
render() {
return(
<h1>{this.input}</h1>
);
}
}
Solution
Simply render props in the child
class ChildComponent extends Component{
render() {
return(
<h1>{this.props.detail}</h1>
);
}
}
Suggestion
Set initial value to "--" and disable that option in the UI so it isn't selectable
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: "--", // <-- set initial select value
prevValue: "",
submitted: false
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
event.preventDefault();
const { value } = event.target;
this.setState({
prevValue: this.state.value,
submitted: true,
value
});
}
render() {
return (
<div className="Base">
<h1> By Productline </h1>
<form>
<label className="label">
Select Workflow: {" "}
<select value={this.state.value} onChange={this.handleChange}>
<option disabled value="--"> -- </option> // <-- disable so it can't be chosen
<option value="test1">test1</option>
<option value="test2">test2</option>
<option value="test3">test3</option>
</select>
</label>
</form>
<div>
{this.state.value !== "--" && (
<ChildComponent
history={this.props.history}
detail={this.state.value}
/>
)}
</div>
</div>
);
}
}
You need to understand that the problem is in ChildComponent, not your App component.
render() {
return(
<h1>{this.input}</h1>
);
}
As you can see, you always render this.input. It is supposed to be assigned props.detail, but note that assignment only happens in constructor, which means it's not dynamic enough.
So, change your render method on ChildComponent to this.
...
<h1>{this.props.detail}</h1>
...
I'm currently trying to render a drop down list within react that will display a list of times. The user should be able to select a specific value and that value will get updated within the state via onChange(). My issue is that I'm trying to use select, but my drop down list is not showing anything when I click on it. Any help would be appreciated, thanks.
This is what I have currently
myfile.js
export default class MyClass extends Component {
constructor(props){
super(props);
this.state = {
times: ["1:00", "2:00", "3:00"]
}
}
RenderList(){
let tempArray = [];
let times = this.state.times;
for(var i = 0; i < times.length; i++){
tempArray.push(<option>{times[i]}</option>)
}
return tempArray
}
return (
<div
<select>{this.RenderList()}</select>
</div>
);
}
I kind of expected to see a list of times in the drop down list once it was rendered, but the list is null.
Is this what you are looking for? You need map to render the options, I hope this helps!
class App extends Component {
constructor(props) {
super(props);
this.state = {
times: ["1:00", "2:00", "3:00"]
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(e) {
console.log(e.target.value)
}
render() {
const {times} = this.state;
return (
<select onChange={this.handleChange}>
{times.map(time => {
return (
<option value={time}> {time} </option>
)
})}
</select>
)
}
}
render(<App />, document.getElementById('root'));
I think I would do it this way.
import React, {Component} from 'react'
class ComponentName extends Component{
state = {
myArray: ["1:00", "2:00", "3:00"]}
}
render() {
return (
<select>
{this.state.myArray.map(optn => (
<option>{optn}</option>
))}
</select>
)
}
export default ComponentName
Hope this helps
I have the following Codesandbox.io:
https://codesandbox.io/s/qxkq5vvm1q
which is a basic ReactJS / Redux application.
The key components here are:
a Select which gets its values something like through this way: Redux (state manager) -> PanelMaterialSize (container) -> Select
one Updater component which takes care of update the values available on the Select through Redux
Alert button, which when clicked should alert the value stored on the store
What should happen is:
when the user changes an option on the Select, that value should be stored on the store. This is actually happening properly - OK
if the Select gets its values changed (for example because the Updater component), then it should automatically change the value stored on the store with the value it is showing (something similar as if the user changes the value on it). Unfortunately this is not happening - The Goal
Here are some of the codes:
./src/controls/Select/Select.js
import React, { Component } from "react";
import "./Select.scss";
class Select extends Component {
constructor(props) {
super(props);
let { name, data, className, ...controlProps } = this.props;
this.name = name;
this.data = data;
this.controlProps = controlProps;
this.state = {
[name]: data,
className
};
}
render() {
let data = this.state[this.name];
return (
<div className="control-select" {...this.controlProps}>
<div className="custom-dropdown custom-dropdown--grey">
<select className="custom-dropdown__select custom-dropdown__select--grey">
{this.props.data.length > 0 &&
this.props.data.map((elem, index) => {
return (
<option value={elem.value} key={index}>
{elem.text}
</option>
);
})}
</select>
</div>
</div>
);
}
}
export default Select;
src/controls/PanelMaterialSize/PanelMaterialSize.js
import React, { Component } from "react";
import { connect } from "react-redux";
import "./PanelMaterialSize.scss";
import Select from "../Select/Select";
import { setThemeList, setSelectedTheme } from "../../store/AppConfig/actions";
class PanelMaterialSize extends Component {
constructor(props) {
super(props);
this.state = {
selection: "",
options: []
};
}
handleChange = e => {
let target = e.target;
let value = target.value;
this.props.setSelectedTheme(value);
};
render() {
return (
<div className="partial-designer-panel-material-size">
<div>
<div className="label-input">
<div className="label">THEME</div>
<div className="input">
<Select
name="selection"
value={this.state.selection}
data={this.props.themeList}
style={{ width: "100%" }}
onChange={this.handleChange}
/>
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = appState => {
return {
themeList: appState.appConfig.themeList,
selectedTheme: appState.appConfig.selectedTheme,
};
};
const mapDispatchToProps = dispatch => {
return {
setThemeList: themeList => dispatch(setThemeList(themeList)),
setSelectedTheme: selectedTheme => dispatch(setSelectedTheme(selectedTheme)),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(PanelMaterialSize);
Any idea on how to make the point 2 work?
If possible, please, provide back your solution on a forked Codesandbox.io.
Thanks!
Updater component is producing new list of themes every 3seconds
It must also dispatch setSelectedTheme action to update selected theme in application state
Background
I am going to be filtering a table based on the values in a selection box. I am having trouble understanding state and props within react.js. Once I pass the value in I can easily do the filtering but I am trying to do this the "react" way.
Question
How do I pass the value of SelectionBox when it is selected or changed to TableDisplay when the user selects one of the options?
Example
class SelectionBox extends React.Component {
render() {
return <div className="filter">
<label for="business">Filter by Status
<select id="business" name="business">
<option value="all">All Requests</option>
<option value="approved">Approved</option>
<option value="pending">Pending</option>
<option value="denied">Denied</option>
</select>
</label>
</div>;
}
}
class TableDisplay extends React.Component {
render() {
return <div className="wrapper">
<h1>Requests</h1>
<SelectionBox />
<div><table className="table">
<tr className="seperate"><td>Title</td><td>Status</td><td>Created</td><td>Updated</td><td>Delete</td></tr>
{Object.keys(requests).map(function(key) {
let styling = "bg-plain";
if (requests[key].status == "Approved") {
styling = "bg-success";
} else if (requests[key].status == "Denied") {
styling = "bg-danger";
}
return <tr className={styling}>
<td>{requests[key].title}</td>
<td>{requests[key].status}</td>
<td>{requests[key].created_at.slice(0, 10)}</td>
<td>{requests[key].updated_at.slice(0, 10)}</td>
<td>Delete</td>
</tr>;
})}
</table>
</div></div>;
}
}
Research
From reading I think what I need to implement here is,
Inside SelectionBox
constructor(props) {
super(props);
this.state = {
// Something referring to the state of the select box here
};
};
Then access props from within TableDisplay?
First, to clear up your understanding of state vs props you should reference this answer: What is the difference between state and props in React?
Second, to pass the value of the SelectionBox to the TableDisplay you need to create some kind of parent TableDisplayContainer component that contains both components. The TableDisplayContainer will store the value of the select dropdown in its state. To do this, you need to pass a function as a prop to the SelectionBox that will handle the onChange event of the select dropdown (you can call it handleOnChange for example). The handleOnChange method will set the value to the state of the TableDisplayContainer. Once it's set in state, you can pass the value to the TableDisplay component as a prop.
Here's a quick example of what you can do:
class SelectionBox extends React.Component {
render() {
return (
<div className="filter">
<label for="business">Filter by Status
<select
id="business"
name="business"
onChange={this.props.handleOnChange}
>
<option value="all">All Requests</option>
<option value="approved">Approved</option>
<option value="pending">Pending</option>
<option value="denied">Denied</option>
</select>
</label>
</div>
);
}
}
class TableDisplay extends React.Component {
render() {
// Do your filtering with this value
const {selectValue} = this.props;
return (
<div className="wrapper">
<h1>Requests</h1>
<SelectionBox />
<div><table className="table">
<tr className="seperate"><td>Title</td><td>Status</td><td>Created</td><td>Updated</td><td>Delete</td></tr>
{Object.keys(requests).map(function(key) {
let styling = "bg-plain";
if (requests[key].status == "Approved") {
styling = "bg-success";
} else if (requests[key].status == "Denied") {
styling = "bg-danger";
}
return <tr className={styling}>
<td>{requests[key].title}</td>
<td>{requests[key].status}</td>
<td>{requests[key].created_at.slice(0, 10)}</td>
<td>{requests[key].updated_at.slice(0, 10)}</td>
<td>Delete</td>
</tr>;
})}
</table>
</div>
</div>
);
}
}
class TableDisplayContainer extends React.Component {
constructor() {
super();
this.state = {
selectValue: 'all' // use this as default
}
}
handleOnChange(e) {
this.setState({
selectValue: e.target.value
});
}
render() {
const {selectValue} = this.state;
return (
<div>
<SelectionBox
handleOnChange={this.handleOnChange.bind(this)}
/>
<TableDisplay
selectValue={selectValue}
/>
</div>
)
}
}
On React state is something that is relevant to the component itself, and props are passed down to it (or have a default value in case of omission). The handle events guide explains my solution below:
You can pass a onChange handler to selectionBox and use it on your TableDisplay component
class SelectionBox extends React.Component {
render () {
//...
<select onChange={this.props.onChange}>
//...
</select>
//...
}
}
SelectionBox.propTypes = {
onChange: PropTypes.func.isRequired
}
class TableDisplay extends React.Component {
constructor(props) {
super(props)
this.onSelection = this._onSelection.bind(this)
this.state = {
selectValue: null
}
}
_onSelection (ev) {
this.setState({selectValue:ev.target.value})
}
render () {
//...
<SelectionBox onChange={this.props.onSelection} />
//...
<h1>{'The select value is '+ this.state.selectValue}</h1>
}
}
Notice that I used propTypes just to make sure that I don't forget.
The InputField & Button are custom components that go into a form to create a form. My issue is how do I send the data back up to form so that on button click, I can fire ajax on the form with data (username & password):
export default auth.authApi(
class SignUpViaEmail extends Component{
constructor(props){
super(props);
this.state = {
email : "",
password : ""
};
this.storeEmail = this.storeEmail.bind( this );
this.storePassword = this.storePassword.bind( this );
}
storeEmail(e){
this.setState({ email : e.target.value });
}
storePassword(e){
this.setState({ password : e.target.value });
}
handleSignUp(){
this.props.handleSignUp(this.state);
}
render(){
return(
<div className="pageContainer">
<form action="" method="post">
<InputField labelClass = "label"
labelText = "Username"
inputId = "signUp_username"
inputType = "email"
inputPlaceholder = "registered email"
inputClass = "input" />
<Button btnClass = "btnClass"
btnLabel = "Submit"
onClickEvent = { handleSignUp } />
</form>
</div>
);
}
}
);
Or Is it not recommended & I should not create custom child components within the form?
child component => InputField
import React,
{ Component } from "react";
export class InputField extends Component{
constructor( props ){
super( props );
this.state = {
value : ""
};
this.onUserInput = this.onUserInput.bind( this );
}
onUserInput( e ){
this.setState({ value : e.target.value });
this.props.storeInParentState({[ this.props.inputType ] : e.target.value });
}
render(){
return <div className = "">
<label htmlFor = {this.props.inputId}
className = {this.props.labelClass}>
{this.props.labelText}
</label>
<input id = {this.props.inputId}
type = {this.props.inputType}
onChange = {this.onUserInput} />
<span className = {this.props.validationClass}>
{ this.props.validationNotice }
</span>
</div>;
}
}
Error : I get the error e.target is undefined on the parent storeEmail func.
React's one-way data-binding model means that child components cannot send back values to parent components unless explicitly allowed to do so. The React way of doing this is to pass down a callback to the child component (see Facebook's "Forms" guide).
class Parent extends Component {
constructor() {
this.state = {
value: ''
};
}
//...
handleChangeValue = event => this.setState({value: event.target.value});
//...
render() {
return (
<Child
value={this.state.value}
onChangeValue={this.handleChangeValue}
/>
);
}
}
class Child extends Component {
//...
render() {
return (
<input
type="text"
value={this.props.value}
onChange={this.props.onChangeValue}
/>
);
}
}
Take note that the parent component handles the state, while the child component only handles displaying. Facebook's "Lifting State Up" guide is a good resource for learning how to do this.
This way, all data lives within the parent component (in state), and child components are only given a way to update that data (callbacks passed down as props). Now your problem is resolved: your parent component has access to all the data it needs (since the data is stored in state), but your child components are in charge of binding the data to their own individual elements, such as <input> tags.
Addendum
In response to this comment:
What if we render a list of the child component? Using this single source of truth in Lifting state up technique will let the parent controls all the state of all the child inputs right? So how can we access each of the value input in the child component to (which is rendered as list) from the parent component?
For this case, you may map a child component for each element in the list. For example:
class Parent extends Component {
//...
handleChangeListValue = index => event => {
this.setState({
list: this.state.list
.map((element, i) => i === index ? event.target.value : element)
});
}
//...
render() {
return this.state.list.map((element, i) => (
<Child
value={element}
onChangeValue={this.handleChangeListValue(i)}
/>
));
P.S. Disclaimer: above code examples are only for illustrative purposes of the concept in question (Lifting State Up), and reflect the state of React code at the time of answering. Other questions about the code such as immutable vs mutable array updates, static vs dynamically generated functions, stateful vs pure components, and class-based vs hooks-based stateful components are better off asked as a separate question altogether.
React class component
Parent.js
import React, { Component } from 'react';
import Child from './child'
class Parent extends Component {
state = {
value: ''
}
onChangeValueHandler = (val) => {
this.setState({ value: val.target.value })
}
render() {
const { value } = this.state;
return (
<div>
<p> the value is : {value} </p>
<Child value={value} onChangeValue={this.onChangeValueHandler} />
</div>
);
}
}
export default Parent;
Child.js
import React, { Component } from 'react';
class Child extends Component {
render() {
const { value , onChangeValue } = this.props;
return (
<div>
<input type="text" value={value} onChange={onChangeValue}/>
</div>
);
}
}
export default Child;
React hooks
Parent.js
import { useState } from "react";
import Child from "./child";
export default function Parent() {
const [value, changeValue] = useState("");
return (
<div>
<h1>{value}</h1>
<Child inputValue={value} onInputValueChange={changeValue} />
</div>
);
}
Child.js
export default function Child(props) {
return (
<div>
<input
type="text"
value={props.inputValue}
onChange={(e) => props.onInputValueChange(e.target.value)}/>
</div>
);
}
Parent.js
import SearchBar from "./components/SearchBar";
function App() {
const handleSubmit = (term) => {
//Log user input
console.log(term);
};
return (
<div>
<SearchBar onPressingEnter={handleSubmit} />
</div>
);
}
export default App;
Child.js
import { useState } from "react";
function SearchBar({ onPressingEnter }) {
const [UserSearch, setname] = useState("[]");
/* The handleChange() function to set a new state for input */
const handleChange = (e) => {
setname(e.target.value);
};
const onHandleSubmit = (event) => {
//prevent form from making a http request
event.preventDefault();
onPressingEnter(UserSearch);
};
return (
<div>
<form onSubmit={onHandleSubmit}>
<input
type="search"
id="mySearch"
value={UserSearch}
onChange={handleChange}
name="q"
placeholder="Search the siteā¦"
required
/>
</form>
</div>
);
}
export default SearchBar;
You can add a "ref name" in your InputField so you can call some function from it, like:
<InputField
ref="userInput"
labelClass = "label"
labelText = "Username"
inputId = "signUp_username"
inputType = "email"
inputPlaceholder = "registered email"
inputClass = "input" />
So you can access it using refs:
this.refs.userInput.getUsernamePassword();
Where getUsernamePassword function would be inside the InputField component, and with the return you can set the state and call your props.handleSignUp