I have two components one called ReturningCustomer and the other one called Update. I have a phone number saved in state in ReturningCustomer and I want to pass that phone number to the Update component with having to render Update or use Redux:
render() {
return (
<div className="row returning-customer">
<h3>Returning Customer</h3>
<div className="col">
<p className="error">{this.state.error}</p>
<form className="row" onSubmit={this.onSubmit}>
<label>Phone Number</label>
<input
type="text"
className="validate"
onChange={this.onPhoneNumberChange}
/>
<button className="btn">Go</button>
</form>
</div>
<Update
phoneNumber={this.state.phoneNumber}
/>
</div>
);
}
In the render function for Update you could use either one of the following and the component would not render
render() {
return false;
}
render() {
return null;
}
render() {
return [];
}
render() {
return <></>;
}
It's not entirely clear what you're asking based on the current description, but it sounds like you're either wanting to conditionally render a component while passing props, or you want to know how to use those props? I'm including both here.
Conditionally rendering a component and passing props:
// SomeComponent.js
import React from 'react'
import Update from '../..'
class SomeComponent extends React.Component {
state = { phoneNumber: '' };
renderUpdate() {
const someCondition = true;
if (someCondition) {
return <Update phoneNumber={this.state.phoneNumber} />
}
return null
}
render() {
return (
<div>
{this.renderUpdate()}
</div>
)
}
}
export default SomeComponent
Using props:
// Update.js
import React from 'react'
// destructuring the props object here
const Update = ({ phoneNumber }) => (
<div>
<p>{phoneNumber}</p>
</div>
)
export default Update
Related
I am building a simple react app and I am beginner. I am trying to send state as props to another component But when I access props like this.props.name then it is showing undefined.
App.js:
class Home extends React.Component {
state = {
fields : {
name : '',
}
}
updateField = evt => {
const fields = Object.assign({}, this.state.fields);
fields[evt.target.name] = evt.target.value;
console.log(evt.target.value)
this.setState({fields})
}
render() {
return(
<div>
<form>
<input
name="name"
id="name_id"
onChange={this.updateField}
value={this.state.fields.name}
/>
</form>
<Link to='/second_component'><SecondComponent name={this.state.fields} />Submit</Link>
<Routes>
<Route path='/second_component' element={<SecondComponent/>} />
</Routes>
</div>
)
}
}
class SecondComponent extends React.Component {
render() {
return (
<div style={{color: "white"}}>
{console.log(this.props.name)}
</div>
)
}
}
export default Home;
I have tried many times but it is still not working. When I try to console.log then it is showing "undefined".
Change fields[evt.target.name] = evt.target.value; to fields.name = evt.target.value; and name={this.state.fields} to name={this.state.fields.name}. Should work.
I'm having a trouble updating the state. I'm using redux so the state is updated on the reducer level. After updating the state from another component a new state is returned with new data. mapStateToProps is called but the component is not re-rendering.
This is my component
class Users extends Component {
render() {
console.log("RENDERING ------");
const usernames= this.props.usernames.map((username, key) => {
return (<div key={key} className="card mt-2">
<div className="card-body">
{username}
</div>
</div>)
})
return (
<div data-spy="scroll" data-target="#navbar-example3" data-offset="0">
{usernames}
</div>
)
}
}
const mapStateToProps = state => {
console.log("STATE", state);
return {
usernames: state.usernames.data
}
}
export default connect(mapStateToProps, null)(Users);
when loading the component the usernames are displayed. but adding a new username from another component the mapStateToProps is called but the component is not re-rendered.
Parent Component
class Parent extends Component {
render() {
return (
<div className="container">
<div className="row">
<div className="col">
<Editor />
</div>
<div className="col">
<Users />
</div>
</div>
</div>
)
}
This is the editor component where I'm dispatching the action
class Editor extends Component {
state = {
user: ""
}
handleChange = event => {
this.setState({ user: event.target.value });
}
onSubmit = e => {
e.preventDefault();
this.props.addUser(this.state.user);
}
render() {
return (
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label htmlFor="exampleFormControlTextarea1">User</label>
<textarea className="form-control" id="exampleFormControlTextarea1" rows="3" value={this.state.user} onChange={this.handleChange} ></textarea>
</div>
<button type="submit" className="btn btn-primary mb-2">Submit</button>
</form>
)
}
}
const mapDispatchToProps = dispatch => {
return {
addUser: (user) =>
dispatch(addUser(user))
}
}
export default connect(null, mapDispatchToProps)(Editor);
After trying and searching online. I discovered that the problem was that the component re-render if mapStateToProps returns a different value from the last call. For my case I was handling my state on a mutable way. I was using push to add new user to the state on the reducer. The solution is to do it in the immutable way using concat:
const us = [];
us.push(action.results.user);
return { ...state, users: state.users.concat(us) };
You are mapping the usernames prop in your mapStateToProps, but not making use of it in your component. You are in fact creating a new variable called usernames and assigning it to the result of a map on this.props.messages.
Forgive me if I'm not understanding what you're trying to do, but it looks like you should just make use of this.props.usernames, and then when you update it through a dispatch, the prop will update and in turn the component will re-render.
I currently have a reducer that does a deep copy of state and returns it with the updated value.
function countableItems(state = initialState, action) {
switch (action.type) {
case types.ADD_TO_SUM:
let denomMap = findDenomination(state.denomGroups, action),
nestedCopy = Immutable.fromJS(state);
return nestedCopy.setIn(['denomGroups', denomMap.group, denomMap.key, denomMap.index, 'sum'], parseFloat(action.value)).toJS();
default:
return state;
}
}
In my render function of the display Component I see the correct updated values in this.props.denoms The render() function builds up child <DenomInput> components, and when I set my breakpoints I see the correct data being passed in
render() {
let denomGroups = this.props.denoms.map((denom, i) => {
return (
Object.keys(denom).map((key) => {
let denoms = denom[key].map((item, i) => {
return <DenomInput denom={item} onDenomChange={this.onDenomChange} key={i}></DenomInput>
});
return (<div className="col"><h2>{key}</h2>{denoms}</div>)
})
);
});
return (
<div className="countable-item-wrapper">
<div className="row">
{denomGroups}
</div>
</div>
);
}
However when the <DenomInput> components render it renders the same value as what they were initially set
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class DenomInput extends Component {
constructor(props) {
super(props);
this.state = { denom: props.denom }
this.handleKeyUp = this.handleKeyUp.bind(this);
}
handleKeyUp = (e) => {
this.props.onDenomChange(e.target.value, this.state.denom.name);
}
render() {
return (
<div className="input-group denom">
<span className="input-group-addon">{this.state.denom.label}</span>
<input
type="text"
className="form-control"
onChange={this.handleKeyUp}
value={this.state.denom.sum} />
<span className="input-group-addon">{this.state.denom.count | 0}</span>
</div>
);
}
}
DenomInput.PropTypes = {
denom: PropTypes.object.isRequired,
onDenomChange: PropTypes.function
}
export default DenomInput;
What piece am I missing to update the view with React and Redux?
May be componentWillReceiveProps can do the trick. It will update the state of the component whenever new data is receive from parent, and call the render function again.
Try
class DenomInput extends Component {
...
componentWillReceiveProps(nextProps) {
this.setState({ denom: nextProps.denom })
}
...
}
It looks like you're seeding your initial state with the props from your store. You then render from the component state, but you never update the component state. They only get set once because constructor is only called once the component is rendered. To fix, either remove this component state entirely and just connect it to the redux store, or update the component state onChange. I recommend removing the local state. I have found that keeping the two states in sync is error-prone.
constructor(props) {
super(props);
this.state = { denom: props.denom }
this.handleKeyUp = this.handleKeyUp.bind(this);
}
handleKeyUp = (e) => {
this.props.onDenomChange(e.target.value, this.state.denom.name);
this.setState({ denom: /*new state identitcal to change in redux store*/ })
}
edit2: An example of raising state up. The steps are:
1. Connect one of your parent components and grab the appropriate slice of state with a mapStateToProps function.
2. Pass the props through your connected parent component to DenomInput.
4. In this.denomsChange, dispatch the appropriate action. It is unclear what this is since you did not include your action in the post.
class DenomInput extends Component {
...
render() {
return (
<div className="input-group denom">
<span className="input-group-addon">{this.props.denom.label}</span>
<input
type="text"
className="form-control"
onChange={this.handleKeyUp}
value={this.props.denom.sum} />
<span className="input-group-addon">{this.props.denom.count | 0}</span>
</div>
);
}
}
export default DenomInput;
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
So I have a parent component and a log in component.
I want the user to enter their details and then hit submit and then store/pass those details around so they can be used by other components.
how is this best done in React?
for example I have this input field inside my log in component
<p>
<input type="text" id="playerName" value={this.props.nameValue} onChange={this.props.handleNameChange}/>
</p>
Then I want to pass the value that is entered to the parent component
I have this function in my parent component:
handleNameChange(event){
this.setState({nameValue: event.target.value})
};
and in my return I have:
return (
<div>
<LoginPage handleClick={this.handleClick.bind(this)} handleNameChange={this.handleNameChange.bind(this)}/>
</div>
)
However, when I console.log(nameValue) I get undefined. any ideas? can add more code if necessary/relevant
From your example you never pass nameValue to the child component.
Updated your example of rendering the LoginPage, passing this.state.nameValue into the child component via props:
return (
<div>
<LoginPage
handleClick={this.handleClick.bind(this)}
handleNameChange={this.handleNameChange.bind(this)}
nameValue={this.state.nameValue}
/>
</div>
)
Your approach using state and props is fine. Are you sure that you shouldn't just be using...
console.log(this.state.nameValue);
This is a working example
class Parent extends React.Component {
constructor() {
super();
this.state = {
nameValue:''
};
}
render() {
return (
<Child handleClick={this.handleClick.bind(this)} handleNameChange={this.handleNameChange.bind(this)} nameValue={this.state.nameValue} />
);
}
handleNameChange(e) {
this.setState({
nameValue: e.target.value
});
}
handleClick() {
alert(this.state.nameValue);
}
}
class Child extends React.Component {
render() {
return (
<div>
<input type="text" value={this.props.nameValue} onChange={this.props.handleNameChange} />
<button onClick={this.props.handleClick}>Click Me!</button>
</div>
);
}
}
JSFiddle here.