Update state from other Component to Main Component - reactjs

I created a component/file in my React-Project, so that I can better organize it.
I have exported a Login-Component <SignInSide></SignInSide> and imported it in my main file:
Main-File:
export default class Login extends Component {
constructor(props) {
super(props);
this.onChangeUsername = this.onChangeUsername.bind(this);
this.state = {
username: ""
};
}
onChangeUsername(e) {
this.setState({
username: e.target.value
});
}
render() {
return (
<SignInSide></SignInSide>
);
}
}
This is my very basic Login-Component. As I already said, I just want to call this.onChangeUsernam when I imported this as <SignInSide></SignInSide> but i dont know what I have to write inside the onClick-Argument inside of the Login-Component to get/update the state from the Main-File.
Login-Component
export default function SignInSide(props) {
return (
<form className={classes.form}>
<TextField/>
<Button>
Login
</Button>
</form>
);
}
I am very thankful for helping. Can you give me a short and easy to understand example, so that I can add this by myself to my project? I only have to understand, what i have to do.

To update the Login component's state, you need to pass props from parent component to child component (passing props from Login to SignInSide component).
Therefore, you need to pass onChangeUsername method as a prop to the <SignInSide/> component. Inside SignInSide component, you need to manage a local state to keep the text input that you entered. It only needs to be used when you're submitting the data which trigger the onClick function onChangeUsername which has been passed from parent component.
Main-File:
import { Component } from "react";
import SignInSide from "./SignInSide";
export default class Login extends Component {
constructor(props) {
super(props);
this.onChangeUsername = this.onChangeUsername.bind(this);
this.state = {
username: "",
};
}
onChangeUsername(data) {
this.setState({
username: data,
});
}
render() {
console.log("username updated: ", this.state.username);
return <SignInSide onChangeUsername={this.onChangeUsername} />;
}
}
SignInSide Component:
import { useState } from "react";
export default function SignInSide(props) {
const [data, setData] = useState("");
const handleChange = (e) => setData(e.target.value);
return (
<form>
<label>
User Name:
<input type="text" value={data} onChange={handleChange} />
</label>
<button type="button" onClick={() => props.onChangeUsername(data)}>
Submit
</button>
</form>
);
}
Application View
Check the console logs as shown in the application view to identify whether username is updated in main component.

Related

Show placeholder text in ReactJS input until click then show "typing"

Once I click the text box some of my div will show a "typing." Before clicking, it should show "this is just a sample."
<input type="text" onClick={this.state}>
Although your question is really messy, here's the sample code for you. Maybe it'll point you the right direction.
import React from "react";
export default class Test extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
someState: ""
}
this.onChangeState = this.onChangeState.bind(this);
}
onChangeState(e) {
this.setState({
someState: //logic here
})
}
render() {
const {someState} = this.state;
return (
<>
<input type="text" onClick={e => this.onChangeState(e)}>
State value: {someState}
</>
)
}
}
Whenever you click chosen div, you'll call onChangeState method where you can manipulate your state. Don't forget that every state update causes rerender.
For more info visit official React docs.
if I understood correctly you want to display the word 'Typing' once you click on the div.
I think this will help you
import React from "react";
export default class Typing extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
yourStateName: ""
}
this.onChangeState = this.onChangeState.bind(this);
}
onChangeState(e) {
this.setState({
yourStateName: "Typing"
})
}
render() {
const {yourStateName} = this.state;
return (
<div>
<input type="text" onClick={e => this.onChangeState(e)}>
value: {yourStateName}
<div/>
)
}
}

Updating React child component after state change

I made a text input box which, when submitted, will update the state of the messages in the parent component. The messages in the parent component are passed down to the message display. I'd like the component responsible for displaying the messages to update and display the messages after each submission, but can't figure out how to do it. I made a code sandbox here:
https://codesandbox.io/s/unruffled-pasteur-nz32o
Here's my code:
Parent component:
import React, { Component } from "react";
import Messages from "./Messages";
import Input from "./Input";
export default class Container extends Component {
constructor(props) {
super(props);
this.state = {
messages: []
};
}
updateMessage(message) {
this.state.messages.push(message);
}
render() {
return (
<div>
<Messages messages={this.state.messages} />
<Input updateMessage={message => this.updateMessage(message)} />
</div>
);
}
}
Message input component:
import React, { Component } from "react";
export default class Input extends Component {
constructor(props) {
super(props);
this.state = {
message: ""
};
}
sendMessage() {
this.props.updateMessage(this.state.message);
this.setState({ message: "" });
}
render() {
return (
<div>
<input
type="text"
value={this.state.message}
onChange={({ target }) => {
this.setState({ message: target.value });
}}
/>
<button onClick={() => this.sendMessage()}>Send</button>
</div>
);
}
}
Message display component:
import React, { Component } from "react";
export default class Messages extends Component {
render() {
return this.props.messages.map(message => {
return <div>{message}</div>;
});
}
}
Thanks!
From your code:
updateMessage(message) {
this.state.messages.push(message);
}
You're modifying the state directly and you're not supposed to do that (except for in the constructor). It won't cause a re-render in this way. Instead, clone the state, modify it, then update the state via setState. Calling setState will invoke a re-render.
updateMessage(message) {
this.setState({
messages: [...this.state.messages, message],
});
}
In your updateMessage(message) method, can you try:
updateMessage(message) {
let { messages } = this.state;
messages.push(message)
this.setState({ messages })
}
Your error is in this part
updateMessage(message) {
this.state.messages.push(message);
}
You can not change state directly. You must use setState() method to change state
updateMessage(message) {
this.setState({
messages : [...this.state.messages, message]
});
}

React state asynchrous in render

I have small asynchronous problems with render method.
I init my state variable textInRenameTodoPopOverInput with a props variable.
The problem is that when my props variable is changed, the state variable always remains at the initially given value (for this example it will be 'dummyText') so that when the value of my props this.props.todoToRename.text changes, the console always returns :
>> Props in Modal {this.props.todoToRename.text} // Here the props has the expected new value
>> State in modal dummyText // But the state is not refresh and keep 'dummyText'
here is my code that I simplified :
import React from "react";
export default class RenameTodoPopOver extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
textInRenameTodoPopOverInput: this.props.todoToRename.text,
}
}
handleChange = (event) => {
this.setState({ textInRenameTodoPopOverInput: event.target.value });
}
render() {
console.log('Props in Modal ' + this.props.todoToRename.text);
console.log('State in modal ' + this.state.textInRenameTodoPopOverInput);
return (
<input type="text" defaultValue={this.state.textInRenameTodoPopOverInput} onChange={this.handleChange} />
);
}
}
Do you have any idea what my mistake is or how i can resolve this problem ?
You should use getDerivedStateFromProps to update the component state based op props.
export default class RenameTodoPopOver extends React.Component {
state = { textInRenameTodoPopOverInput: '' }
static getDerivedStateFromProps(nextProps){
if(nextProps.todoToRename.text){
return {
textInRenameTodoPopOverInput: nextProps.todoToRename.text
}
}
}
handleChange = (event) => {
this.setState({ textInRenameTodoPopOverInput: event.target.value });
}
render() {
console.log('Props in Modal ' + this.props.todoToRename.text);
console.log('State in modal ' + this.state.textInRenameTodoPopOverInput);
return (
<input type="text" defaultValue={this.state.textInRenameTodoPopOverInput} onChange={this.handleChange} />
);
}
}
some problems with your code:
1. you are using the props and assigning it to the state which is bad.
2. you can shift the "state" to the parent component and let the parent component send the text and handle function as props
//in the parent component
import React from "react";
export default class ParentRenameTodoPopOver extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
textInRenameTodoPopOverInput: this.props.todoToRename.text,
}
}
handleChange = (event) => {
this.setState({ textInRenameTodoPopOverInput: event.target.value });
}
render() {
return (
<RenameTodoPopOver
textInRenameTodoPopOverInput=
{this.state.textInRenameTodoPopOverInput}
handleChange = {this.handleChange}
/>
);
}
}
export default ParentRenameTodoPopOver
now in the child component get the state and handle from the parent as props and work on it.
//in the child component
import React from "react";
const RenameTodoPopOver = (props)=>
return (
<input
type="text"
defaultValue={props.textInRenameTodoPopOverInput}
onChange={props.handleChange} />
)
}
export default RenameTodoPopOver;
while designing components in react, it is easier to write if we divide the components into purely funtional and purely presentational components. Here the parent component is purely functional i.e., it has all the logic on the other hand, the child component is purely functional, it has not logic and it gets all functions and state passed to it from the parent by props.

Check state from imported component

Much of a basic question. Can I pass the state property to another component? So if I create a login app and after a successful login from API call I set the state of loggedInUser: 12345 in say a component called Login.js
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
data:[],
loggedInUser: 12345
}
}
render(){
return(
//Return the this.state.loggedInUser
)
}
}
How can I pass this.state.loggedInUser from Login.js to another component where I've imported Login.js?
For example, in my Page1.js I have import Login from './Login'
Can something like this be achieved? I just want to pass the this.state.loggedInUser value to any page where it us imported.
Thanks.
As mentioned in the comment above, redux is probably best practice here. But here is an example of accomplishing this with vanilla react.
export default class Parent extends Component {
constructor(props) {
super(props)
this.state = {
logginUser: undefined
}
this.handleUserState = this.handleUserState.bind(this);
}
handleUserState = (userInfo) => {
this.setState({logginUser: userInfo})
}
render = () => {
return (
<div>
<Login handleUserState={this.handleUserState} />
</div>
)
}
}
export default class Login extends Component {
constructor(props) {
super(props);
this.loginUser = this.loginUser.bind(this);
}
loginUser = (e) => {
e.preventDefault()
<--- make call to api for userInfo here and pass it to the call below --->
this.props.handleUserState(userInfo)
}
render =() => {
return(
<div>
<button type="submit" onClick={this.loginUser} />
</div>
)
}
}
Basically what's happening here since you are importing Login into another component, you will have that 'Parent' component act as the state manager and save the user data at that level. You then pass the function that updates the state to the Login component and call it once you have the user data to update it with.
I hope this helps!

Multiple instances of a component shouldn't share state

Say I had a simple inputfieldcomponent like so:
import React, {PropTypes, PureComponent} from 'react';
import {render} from 'react-dom';
import {connect} from 'react-redux';
import updateInput from '../../actions/inputActions';
require('./SimpleInput.sass');
export class SimpleInput extends PureComponent {
constructor(props, context) {
super(props, context);
}
handleChange (event) {
this.props.updateField(event.target.value);
}
renderField () {
return (
<input type="text" value={this.props.value || ''} onChange={this::this.handleChange} placeholder={this.props.initial_value}/>
)
}
render () {
return(
<span>
{this.renderField()}
</span>
)
}
}
const mapStateToProps = (state) => {
return {value: state.value.value}
}
const mapDispatchToProps = (dispatch) => {
return {
updateInput: (value) => dispatch(updateInput(value))
};
};
AddressInput.propTypes = {
initial_value: PropTypes.string
};
AddressInput.defaultProps = {
initial_value: "What's the value?"
};
export default connect(mapStateToProps, mapDispatchToProps)(SimpleInput);
I then render two instances:
</SimpleInput initial_value='blah'/>
</SimpleInput>
However, when this is rendered, any update to one of the two fields updates both of them (due to redux only allowing for a single state).
What is the canonical way to approach this problem?
Here's one approach Abraham and giving an example to what Dan commented on. Check out this jsBin demonstrating the concept of a component (here it's SimpleInput) keeping its own internal state (such as a default placeholder) while still interacting with a Container (listening to onChange). While the example doesn't use Redux (for simplicity of creating the demo) you could easily substitute onChange handles with action dispatchers.
class SimpleInput extends React.Component {
constructor(props) {
super(props);
this.state = {
defaultPlaceholder: 'Default Placeholder'
}
}
render() {
return (
<input
value={this.props.value}
placeholder={this.props.placeholder || this.state.defaultPlaceholder}
onChange={this.props.onChange || ()=>{} }
/>
)
}
}
class SimpleInputContainer extends React.Component {
constructor(props) {
super(props);
this.changeInput1 = this.changeInput1.bind(this);
this.changeInput2 = this.changeInput2.bind(this);
this.state = {
input1: 'foo',
input2: 'bar',
}
}
changeInput1(e) {
this.setState({
input1: e.target.value
})
console.log(this.state);
}
changeInput2(e) {
this.setState({
input2: e.target.value
})
}
render() {
return (
<div>
<SimpleInput value={this.state.input1} onChange={this.changeInput1} />
<br />
<SimpleInput value={this.state.input2} onChange={this.changeInput2} />
<br />
<SimpleInput />
<br />
<SimpleInput placeholder={'Explicit Placeholder'} />
</div>
);
}
}
ReactDOM.render(<SimpleInputContainer />, document.getElementById('app'));
One of my use cases was an address lookup with an input managing it's value internally (no redux) and a lookup button emitting the value of the input via onClick={this.props.onSave} and in the Container handling all side effects/redux actions etc.
Lastly in your demo code, you're rerendering SimpleInput unnecessarily, which looks like a code smell. Hope that helps.

Resources