I have been trying to implement a scenario where the data is passed from the child component to the parent component.
I am able to pass it but the screen doesn't persist due to the use of the form. I want to know how I can implement it while using form.
import React, { Component } from 'react';
import './App.css';
class FormComponent extends React.Component {
render() {
return (
<form>
<input type="text" name="caption" value={this.props.caption} onChange={(event) => this.props.changeCaption(event.target.value)} />
<button onClick={this.props.onClick}>Add</button>
</form>
);
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
caption: ""
}
}
changeCaption = (caption) => {
this.setState({
caption: caption
})
}
handleClick = () => {
console.log('Send to the list component', this.state.caption);
}
render() {
return (
<div className="App">
<header className="App-header">
<FormComponent
onClick={this.handleClick}
caption={this.state.caption}
changeCaption={this.changeCaption}
/>
</header>
</div>
);
}
}
export default App;
I would really appreciate some help with this. Thank You.
Hard to tell without testing but let me guess that it's link to "normal" behaviour of a form tag when submitted ....
Then you'll have to prevent the event to bubble, try this :
handleClick = (e) => {
e.preventDefault();
console.log('Send to the list component', this.state.caption);
}
if you don't want to submit form on click you can add type="button" attribute to your button:
<button type="button" onClick={this.props.onClick}>Add</button>
Related
I'm in the React work by 2 hours and I have a problem with how the writer separate correctly the component, an example I have these windows
When I click the button "Set" I change the value this.state.nameFramework, If I write all code inside the App component my helloApp work but if I write the code in the separate component it not work in the instant time but for change the value of the variable this.state.nameframework I had reloaded the page.
My code
require('normalize.css/normalize.css');
require('styles/App.css');
import React from 'react';
import InputFramework from 'components/InputFramework';
import ListPerson from 'components/ListPerson';
const list = [
{
objectID: 1,
'name': 'Vincenzo',
'surname': 'Palazzo'
},
{
objectID: 2,
'name': 'Sara',
'surname': 'Durante'
}
];
let name = 'Vincent';
let nameFramework = 'React';
class AppComponent extends React.Component {
constructor(props){
super(props);
this.state = {
list,
name,
nameFramework
};
this.onSelectPerson = this.onSelectPerson.bind(this);
this.onSubmitText = this.onSubmitText.bind(this);
this.onChangeNameFramework = this.onChangeNameFramework.bind(this);
}
onSubmitText(){
this.setState({nameFramework: this.state.nameFramework});
}
onChangeNameFramework(name){
this.state.nameFramework = name;
}
onSelectPerson(name) {
this.setState({name: name});
}
render() {
//This is no good for my programmer style, resolve it please
return (
<div className="index">
<InputFramework
name={this.state.name}
nameFramework={this.state.nameFramework}
onChange={this.onChangeNameFramework}
onClick={this.onSubmitText}
/>
<ListPerson
onClick={this.onSelectPerson}
list={this.state.list}/>
</div>
);
}
}
AppComponent.defaultProps = {
};
export default AppComponent;
Input component
require('normalize.css/normalize.css');
require('styles/App.css');
import React from 'react';
class InputFramework extends React.Component {
constructor(props){
super(props);
}
render() {
//This is no good for my programmer style, resolve it please
//The nameFramework not update
let {onChange, onClick, name} = this.props;
return (
<div className='index'>
<h1>Hello my name is {name} and I'm learning {this.props.nameFramework}</h1>
<from>
<input type='text'
onChange={event => onChange(event.target.value)}/>
<button type='submit' onClick={() => onClick}>Set</button>
</from>
</div>
);
}
}
InputFramework.defaultProps = {};
export default InputFramework;
List component
require('normalize.css/normalize.css');
require('styles/App.css');
import React from 'react';
class ListPerson extends React.Component {
constructor(props){
super(props);
}
render() {
//This is no good for my programmer style, resolve it please
const {onClick, list} = this.props;
return (
<div className="index">
<ul>
{list.map(function(item){
return (
<li key={item.objectID}>
{item.name}
<button type='button' onClick={() => onClick(item.name)}>Select</button>
</li>
)
})}
</ul>
</div>
);
}
}
ListPerson.defaultProps = {
};
export default ListPerson;
I this is a problem to how to write the code, now I ask you that you have more experience than me, can you help me to undestend.
You are trying to change (mutate) state directly in onChangeNameFramework handler.
Mutating state directly can lead to bugs.
State must be changed only by this.setState, so it must be like this:
onChangeNameFramework(name){
this.setState({
nameFramework: name
})
}
Here is the docs:
https://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly
Another problem is in InputFramework component, when you submit a form the page reloads, to prevent it, you should add e.preventDefault() like this:
class InputFramework extends React.Component {
render() {
//This is no good for my programmer style, resolve it please
//The nameFramework not update
let {onChange, onClick, name} = this.props;
const handleClick = (e) => {
e.preventDefault();
onClick();
}
return (
<div className='index'>
<h1>Hello my name is {name} and I'm learning {this.props.nameFramework}</h1>
<form>
<input type='text'
onChange={event => onChange(event.target.value)}/>
<button type='submit' onClick={handleClick}>Set</button>
</form>
</div>
);
}
}
Lastly in AppComponent the following code is redundant, since you are setting the same state:
onSubmitText(){
this.setState({nameFramework: this.state.nameFramework});
}
You already handle the change of framework name in onChangeNameFramework handler.
I think using both onSubmitText and onChangeNameFramework handler seems unnecesary here, only one of them will be enough.
Playground:
https://codesandbox.io/s/blue-frost-qutb0
I am learning React and I am trying to call a function in a child component, that accesses a property that was passed from parent component and display it.
The props receives a "todo" object that has 2 properties, one of them is text.
I have tried to display the text directly without a function, like {this.props.todo.text} but it does not appear. I also tried like the code shows, by calling a function that returns the text.
This is my App.js
import React, { Component } from "react";
import NavBar from "./components/NavBar";
import "./App.css";
import TodoList from "./components/todoList";
import TodoElement from "./components/todoElement";
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: []
};
this.addNewTodo = this.addNewTodo.bind(this);
}
addNewTodo(input) {
const newTodo = {
text: input,
done: false
};
const todos = [...this.state.todos];
todos.push(newTodo);
this.setState({ todos });
}
render() {
return (
<div className="App">
<input type="text" id="text" />
<button
onClick={() => this.addNewTodo(document.getElementById("text"))}
>
Add new
</button>
{this.state.todos.map(todo => (
<TodoElement key={todo.text} todo={todo} />
))}
</div>
);
}
}
export default App;
This is my todoElement.jsx
import React, { Component } from "react";
class TodoElement extends Component {
state = {};
writeText() {
const texto = this.props.todo.text;
return texto;
}
render() {
return (
<div className="row">
<input type="checkbox" />
<p id={this.writeText()>{this.writeText()}</p>
<button>x</button>
</div>
);
}
}
export default TodoElement;
I expect that when I write in the input box, and press add, it will display the text.
From documentation
Refs provide a way to access DOM nodes or React elements created in the render method.
I'll write it as:
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: []
};
this.textRef = React.createRef();
this.addNewTodo = this.addNewTodo.bind(this);
}
addNewTodo() {
const newTodo = {
text: this.textRef.current.value,
done: false
};
const todos = [...this.state.todos, newTodo];
this.setState({ todos });
}
render() {
return (
<div className="App">
<input type="text" id="text" ref={this.textRef} />
<button onClick={this.addNewTodo}>Add new</button>
{this.state.todos.map(todo => (
<TodoElement key={todo.text} todo={todo} />
))}
</div>
);
}
}
In your approach, what you got as an argument to the parameter input of the method addNewTodo is an Element object. It is not the value you entered into the text field. To get the value, you need to call input.value. But this is approach is not we encourage in React, rather we use Ref when need to access the html native dom.
I am using the react-bootstrap Modal, Form and Button.
Desiring the functionality of clicking the button should open the modal with a form inside it. After filling out the form, one clicks a button (on the modal) and it validates the form data and posts it through a REST API.
I got far enough to figure out that my component split should be as follows:
A button component, a modal component and a form component.
What would be the correct way to structure these components in terms of props/state and placing the functions for validating the data? I am having trouble in understanding the child/parent relationship and when it's applicable
Components:
App Component: This is going to be the top level component
Button Component (If its just a button can also be
just a button):
If this is just a button you can keep this has a just a button in App component, if you are willing to reuse this with some custom element place it in a component.
Modal component: This is going to hold your modal like header,body,footer
Form component: This is a component which will hold the form and its validations.
Component Tree:
App Component will contain a state like showModal, we need to have a handler to set this value and this handler gets triggered when the button is clicked.
import FormModal from './FormModal';
class App extends React.Component {
state = {
showModal : false
}
showModalHandler = (event) =>{
this.setState({showModal:true});
}
hideModalHandler = (event) =>{
this.setState({showModal:false});
}
render() {
return (
<div className="shopping-list">
<button type="button" onClick={this.showModalHandler}>Click Me!
</button>
</div>
<FormModal showModal={this.sate.showModal} hideModalHandler={this.hideModalHandler}></FormModal>
);
}
}
Form Modal:
import FormContent from './FormContent';
class FormModal extends React.Component {
render() {
const formContent = <FormContent></FormContent>;
const modal = this.props.showModal ? <div>{formContent}</div> : null;
return (
<div>
{modal}
</div>
);
}
}
export default FormModal;
Hope that helped!
For basic pseudo code
Main Component:
import Modal from './Modal'
class Super extends React.Component {
constructor(){
this.state={
modalShowToggle: false
}
}
ModalPopUpHandler=()=>{
this.setState({
modalShowToggle: !modalShowToggle
})
}
render(){
return(){
<div>
<Button title='ModalOpen' onClick='this.ModalPopUpHandler'> </Button>
<ModalComponent show={this.state.modalShowToggle}>
</div>
}
}
}
ModalPopUp component:
import FormComponent from 'FormComponent'
class ModalComponent extends React.Component {
constructor(props){
super(props)
this.state={
modalToggle: props.show
}
}
render(){
if(this.state.modalToggle){
return(
<div>
<div className='ModalContainer'>
<FormComponent />
</div>
</div>
)
} else {
</div>
}
}
}
Form Component:
import Button from './Button'
class FormComponent extends React.Component {
constructor(){
this.state={
submitButtonToggle: true,
username: ''
}
}
inputHandler=(e)=>{
if(e){
this.setState({
username: e.target.value
})
}
}
render(){
return(
<div>
<input type='text' value='this.state.username' id='username' onChange='inputHandler' />
<Button title='Submit' disabled={this.state.username.length > 0}> </Button>
</div>
)
}
}
Above are the basic superComponent which we have rendered in app/main entry file.
And form || Modal Component. are the child component.
So in modal component I have called the same Form-component.
Here in Form-component input type handler, submit button is disabled from state.. with input string length we are handling its validation.
I hope it works for you.
I have a component (LoginScreen). In that component I want to display my Login component as the first thing the user sees. When user clicks on sign up button, in my Loginscreen component, the Signup Component should be rendered instead. From the signup Component the user finds a button 'Back to Login' and when clicked, again the Login Component should be rendered insight the componentt Loginscreen. Im new to React and trying to follow tutorials about how to share data among parent/child and among siblings but am completely confused. Any help would be amazing!
class Loginscreen extends React.Component{
constructor(props) {
super(props)
this.state = {
status:false
}
this.changeStatus = this.changeStatus.bind(this);
}
changeStatus(status) {
this.setState({
status: true
});
}
render() {
return (
<div>
<Login status={this.state.status}/>
<Signup status={this.state.status}/>
<p>No account yet?</p>
<button onClick={this.changeStatus}>Sign up</button>
// if Signup Component is rendered insight Loginscreen, then this button should also not be rendered.
</div>
)
}
}
class Signup extends React.Component {
...
//where can I even call this function in my case?
handleChange() {
const status:true;
this.props.onClick(status);
}
...
<button><Link to='/loginscreen'>Back to Login</Link></button>
...
}
class Login extends React.Component {
...
...
}
Ok, I believe you are looking for routing?
Solution 1 (recommended):
Using React-Router to handle the routing and the React-Router/Link component will handle the switching.
Solution 2:
Using a simple state routing, saving the view name in the parent component and display the view based on it, also passing a function to update this view:
class App extends React.Component{
constructor(props) {
super(props)
this.state = {
view: 'login' // its login because we want it to be the default
}
this.changeView = this.changeView.bind(this);
}
changeView(view) {
this.setState({
view // ES6, if the key & value variable name the same, just type it once.
});
}
render() {
const { view } = this.state; // thats es6 destructuring, use it to make the variables clean instead of repeating the this.state/this.props
return (
<div>
{
view == 'login'
? (<Login changeView={this.changeView}/>)
: (<Signup changeView={this.changeView}/>)
}
</div>
)
}
}
class Signup extends React.Component {
...
render(){
const { changeView } = this.props;
<div className="Signup">
{/* Signup Form Here */}
<p>Already registered?</p>
{/* Wrapping the event in arrow function to avoid auto invoke */}
<button onClick={() => changeView('login')}>Login</button>
</div>
}
...
}
class Login extends React.Component {
...
render(){
const { changeView } = this.props;
<div className="Login">
{/* Login Form Here */}
<p>No account yet?</p>
<button onClick={() => changeView('signup')}>Sign up</button>
</div>
}
...
}
If there are more than 2 views you can wrap the return in a normal If statement, or move it in a separate method.
or you can use a dynamic component rendering, something like this:
render() {
const { view } = this.state;
const ViewComponent = require(`./views/${view}.jsx`);
return (<div><ViewComponent.default changeView={this.changeView} /></div>);
}
CONTEXT
I'm trying to get the value of an input field from a stateless component inside another stateless component and then use it to call a method. I'm using rebass for my UI component and doing this in Meteor + Mantra.
I understand that I could do this by using refs if I were using <input> HTML fields and not another stateless component.
PROBLEM
My current code yield preventDefault of undefined, and when removed, the console.log prints out each time the input changes, not on submit. I believe that my state applies to the entire Dashboard component, instead of the stateless Rebass <Input/>, but I do not know how to change this.
import React from 'react';
import {PageHeader, Container, Input,Button} from 'rebass';
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.state = { websiteUrl: ''};
this.onInputChange = this.onInputChange.bind(this);
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onInputChange(event) {
this.setState({websiteUrl:event.target.value});
}
onFormSubmit() {
event.preventDefault;
const {create} = this.props;
const {websiteUrl} = this.state.websiteUrl;
console.log(this.state.websiteUrl);
create(websiteUrl);
}
render() {
const { error } = this.props;
return (
<div>
<PageHeader
description="Dashboard Page"
heading="Dashboard"
/>
<Container>
<form>
<Input
value={this.state.websiteUrl}
type="text"
buttonLabel="Add Website"
label="Website"
name="add_website"
onChange={this.onInputChange}
/>
<Button
backgroundColor="primary"
color="white"
inverted={true}
rounded={true}
onClick={this.onFormSubmit()}
> Add Website </Button>
</form>
</Container>
</div>
);
}
}
export default Dashboard;
You should pass an event to the onFormSubmit function:
<Button
backgroundColor="primary"
color="white"
inverted={true}
rounded={true}
onClick={(event) => this.onFormSubmit(event)}
...