Output data from form on the page react - javascript

I am writing todo app. There are main files in my directory now:
App (rendering main page with header and buttons)
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = { triggerText: 'Create a task' };
}
propTypes = {
triggerText: PropTypes.string.isRequired,
handleSubmit: PropTypes.object.isRequired,
};
render() {
const { triggerText } = this.state;
const { handleSubmit } = this.props;
return (
<div className="App">
<header className="App-header">
<h1>To Do List</h1>
<div id="tasksList">
<span className="tasks active">Tasks</span>
</div>
<div id="categoriesList">
<span className="categories">Categories</span>
</div>
<div>
<Container triggerText={triggerText} onSubmit={handleSubmit} /> // creates modal dialog and uses TodoForm
</div>
</header>
<div id="container" className="container">
<TodoBox tasks={[]}/>
</div>
</div>
);
}
}
TodoForm (create a form)
export default class TodoForm extends React.Component {
constructor(props) {
super(props);
this.state = { value: '', tasks: [] };
}
propTypes = {
handleSubmit: PropTypes.object.isRequired,
}
handleRemove = (currentTaskId) => (e) => {
e.preventDefault();
const { tasks } = this.state;
this.setState({ tasks: tasks.filter(({ id }) => id !== currentTaskId) });
};
handleChange = (e) => {
e.preventDefault();
this.setState({ value: e.target.value });
}
handleSubmit = (e) => {
e.preventDefault();
const { value, tasks } = this.state;
const newTask = { id: uniqueId(), text: value };
this.setState({ value: '', tasks: [newTask, ...tasks] });
}
render() {
const { value } = this.state;
return (
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="text"><strong>Create a task</strong></label>
<input
type="text"
onChange={this.handleChange}
value={value}
required
className="form-control"
id="text"
placeholder="I am going..."
/>
</div>
<div className="form-group">
<button type="submit" className="form-control btn btn-primary">Add</button>
</div>
</form>
);
}
}
TodoBox (generating list of tasks)
class Item extends React.Component {
propTypes = {
onRemove: PropTypes.object.isRequired,
task: PropTypes.string.isRequired,
};
render() {
const { task, onRemove } = this.props;
return (
<div className="row">
<div>
<button type="button" className="btn btn-primary" onClick={onRemove}>-</button>
</div>
<div className="col-10">{task.text}</div>
</div>
);
}
}
export default class TodoBox extends React.Component {
constructor(props) {
super(props);
}
propTypes = {
tasks: PropTypes.string.isRequired,
}
render() {
const { tasks } = this.props;
return (
<div className="item">
{tasks.map((task) => (
<div key={task.id}>
<Item task={task} onRemove={this.handleRemove} />
<hr />
</div>
))}
</div>
);
}
}
And the question is: how I can pass the state from TodoForm to TodoBox in App (it is initialize as an empty array now). I want to output tasks at the bottom of the same page in container after header element.

You can create a function (addTodo) in App component and pass it down to the TodoForm component. In TodoForm component you can invoke the addTodo function from props and send the todoValue as arguments props.addTodo(todoValue). In addTodo function in App component you can update the todoValue to state. Once you update the state it will re-render the App component, then the TodoBox component will call with the updated todoValue value.
Note: But it is not best practice. The best practice is to use React Context

Related

Why redux diff says that states are equal even though ther are not

For fun, I decided to write a booking app.
However ran into a problem when I get free rooms and send room data to TabComponent.js for the first time, everything works. But if I try to send the room data to TabComponent.js again, component does not update, but in redux devtools the state has changed. How to solve this problem? For more understanding, see the pictures below.
Component.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from "prop-types"
import { reservationCheck } from '../../../actions/axiosApi'
import { sendReservationProp } from '../../../actions/propActions'
class Form extends Component {
static propTypes = {
free_rooms: PropTypes.array.isRequired,
}
constructor(props) {
super(props)
this.state = {
arrival_date: '',
leaving_date: ''
}
}
handleChange = event => this.setState({ [event.target.name]: event.target.value });
handleSubmit = event => {
event.preventDefault()
this.props.reservationCheck(this.state)
}
sendFreeRoomPropToBookingTab = free_room => {
const reservation_prop = Object.assign(this.state, free_room)
this.props.sendReservationProp(reservation_prop) <--- sending room prop to tabComponent.js
}
render() {
const { arrival_date, leaving_date } = this.state
return (
<div className="booking-form-container">
<form onSubmit={this.handleSubmit}>
<input type="date" name="arrival_date" required onChange={this.handleChange} value={arrival_date} />
<input type="date" name="leaving_date" required onChange={this.handleChange} value={leaving_date} />
<button value="" type="submit">Find</button>
</form>
<div className="reservation-data-result">
{this.props.free_rooms.map(free_room => (
<div className="reservation-data-result__block" key={free_room.id}>
<img src={free_room.main_image} alt="" />
<div className="thumbnail-images">
{free_room.room_image.map(thumbnailImage => {
const [id, src] = thumbnailImage.split(': ')
return (
<div className="thumbnail-image">
<img src={src} key={parseInt(id, 10)}></img>
</div>
)
})}
</div>
<h3>{free_room.name}</h3>
<h5>price: {free_room.price}$</h5>
<a className="booking-btn">
<div className="btn-circle">
<div className="btn-arrow"></div>
</div>
<span className="btn-text" onClick={() => this.sendFreeRoomPropToBookingTab(free_room)}>Book room</span>
</a>
</div>
))}
</div>
</div>
)
}
}
const mapStateToProps = (state) => ({
free_rooms: state.roomReducer.free_rooms
})
export default connect(mapStateToProps, { reservationCheck, sendReservationProp })(Form)
action.js
import { RESERVATION_PROP } from './types'
export const sendReservationProp = reservation_prop => dispatch => {
dispatch({
type: RESERVATION_PROP,
payload: reservation_prop
})
}
reducer.js
import { GET_ROOM_DATA, RESERVATION_CHECK, RESERVATION_PROP } from '../actions/types'
const initialState = {
roomData: [],
free_rooms: [],
reservation_prop: {},
}
export default function (state = initialState, action) {
switch (action.type) {
case GET_ROOM_DATA:
return {
...state,
roomData: action.payload
}
case RESERVATION_CHECK:
return {
...state,
free_rooms: action.payload
}
case RESERVATION_PROP:
return {
...state,
reservation_prop: action.payload
}
default: {
return state
}
}
}
TabComponent.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
class BookingTab extends Component {
constructor(props) {
super(props)
this.state = {
room: this.props.reservation_prop.room,
price: this.props.reservation_prop.price,
arrival_date: this.props.reservation_prop.arrival_date,
leaving_date: this.props.reservation_prop.leaving_date,
name: '',
phone_number: ''
}
}
handleSubmit = () => {
console.log("SUBMIT!")
}
render() {
return (
<div className="booking-tab">
<div className="booking-tab-container">
<h2>Booking</h2>
<form onSubmit={this.handleSubmit}>
<h3>Room: {this.props.reservation_prop.name}</h3>
<h5>Price: {this.props.reservation_prop.price}$</h5>
<span>Arrival date: {this.props.reservation_prop.arrival_date}</span>
<span>Leaving date: {this.props.reservation_prop.leaving_date}</span>
<div className="name-input">
<input type="text" name="name" placeholder="Name" />
</div>
<div className="phone-number-input">
<input type="text" name="phone_number" placeholder="X (XXX) XXX-XX-XX" />
</div>
<button>Submit</button>
</form>
</div>
</div>
)
}
}
const mapStateToProps = state => ({
reservation_prop: state.roomReducer.reservation_prop
})
export default connect(mapStateToProps)(BookingTab)
This line const reservation_prop = Object.assign(this.state, free_room) actually modifies this.state and then sets reservation_prop to this.state.
So as long as this.state is not replaced by a completely new state, you will dispatch the same object multiple times and just modify it - which is something redux cannot detect, as stateBefore === stateAfter will hold true.
You probably want to do
const reservation_prop = Object.assign({}, this.state, free_room)
which creates a new object every time.

How can I take the value of an Input and store it in another component?

I am new to react and I have a problem trying to take the information submitted in an input and return it as output outside the nav component, where it sits. I want the output to return to the Content component but I am having a hard time trying to figure out how to do that. Trying to return it as a prop returns as undefined. I have read the documentation and tried to find answer in videos but nothing seems to be solving the problem. Can anyone point me in the right direction?
// this is the root component
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
userInput: ''
}
}
handleChange = (e) => {
this.setState({
userInput: e.target.value
})
}
render() {
const { userInput } = this.state
return (
<div className="recipes">
<Nav />
<Content userInput={this.state.userInput} changed={this.handleChange} />
</div>
)
}
}
// this is where the input is stored and where I want to take its value and return it to the the Content Component
class Nav extends React.Component {
state = {
userInput: ''
}
handleChange = (e) => {
this.setState({
userInput: e.target.value
})
}
render() {
return (
<nav className="nav">
<h1 className="title" >Nourish</h1>
<h2 className="title" >{this.state.userInput}</h2>
<input type="text" className="input" onChange={this.handleChange} />
</nav>
)
}
}
// this is where I want to output the value to
const Content = (props) => {
console.log(props.userInput)
return (
<h2 className="main"> {props.userInput} </h2>
)
}
you can create a simple input component but the value and event handlers come from the parent as props
look at this example
import React, { useState } from 'react'
import Input from './Input'
const App = () => {
const [InputOneValue, setInputOnValue] = useState("")
const [InputTwoValue, setInputTwoValue] = useState("")
const InputOneChangeHandler = (value) => {
setInputOneValue(value)
}
const InputTwoChangeHandle = (value) => {
setInputTwoValue(value)
}
const onSubmitHanlder = () {
// InputOneValue
// InputTwoValue
}
return (
<form onSubmit={submitHandler}>
<Input value={InputOneValue} changeHandler={InputOneChangeHandler}
<Input value={InputTwoValue} changeHandler={InputTwoChangeHandler}
</form>
)
}
export default App
and this is you Input component
const Input = (props) => {
return <input value={props.value} onChange={props.changeHandler} />
}
export default Input
You dont need to write handle change and not needed to store userInput in Nav. store directly in App.js . so in Nav instead of this.handleChange use this.props.changed this helps you store userInput in App then you can pass the data as props.
// this is the root component
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
userInput: ""
}
}
handleChange = (e) => {
this.setState({
userInput: e.target.value
})
}
render() {
const { userInput } = this.state
return (
<div className="recipes">
<Nav userInput={this.state.userInput} />
<Content userInput={this.state.userInput} changed={this.handleChange} />
</div>
)
}
}
class Nav extends React.Component {
render() {
return (
<nav className="nav">
<h1 className="title" >Nourish</h1>
<h2 className="title" >{this.state.userInput}</h2>
<input type="text" className="input" onChange={this.props.changed} />
</nav>
)
}
}
// this is where I want to output the value to
const Content = (props) => {
console.log(props.userInput)
return (
<h2 className="main"> {props.userInput} </h2>
)
}

Unable to setState on props in react functional component

I have been unable to setState on props I keep getting
TypeError: props.setState is not a function
I'm trying to implement a search function
const HeroComp = (props) => {
let handleSearchSubmit = (e) => {
props.setState({searchValue: e.target.value});
}
return <div className='heroComp' >
<form action="" >
<input type="text" placeholder='search cartigory' onChange={handleSearchSubmit} />
</form>
</div>
}
export default HeroComp;
When I console.log(props) I get
{searchValue: ""}
searchValue: ""
__proto__: Object
This is the parent component
import images from '../data/images'; //the file from which i'm importing images data
class HomePage extends React.Component{
constructor(){
super();
this.state = {
images,
searchValue: ''
}
}
render(){
const {images , searchValue} = this.state;
const filteredImage = images.filter(image => image.cartigory.toLowerCase().includes(searchValue));
return(
<div >
<HeroComp searchValue={ searchValue } />
<GalleryComp filteredImage={filteredImage} />
</div>
)
}
}
export default HomePage;
I know this should be easy but I just can't see the solution .
How about this?
useEffect(() => {
// set the current state
setSearchValue(props.searchValue)
}, [props]);
Functional component dont have state, but you can use reactHooks:
import React, { useState } from 'react';
const HeroComp = (props) => {
let [searchValue, setSearchValue] = useState();
let handleSearchSubmit = (e) => {
setSearchValue(e.target.value);
}
return <div className='heroComp' >
<form action="" >
<input type="text" placeholder='search cartigory' onChange={handleSearchSubmit} />
</form>
</div>
}
export default HeroComp;

Lifting message up in REACT

I want to do a real time searching in React. How can I lift the message up from child to parent? Or how can I pass a parent handler to children through props to handle the event?
parent class:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
activities: activities,
filteredActivities: activities,
};
this.handleSearchChange = this.handleSearchChange.bind(this);
}
filterActivity = searchText => {
return this.state.activities
.filter(activity => {
if(activity.content.toLowerCase().includes(
searchText.toLowerCase())
){
return true;
}
return false;
});
}
handleSearchChange = event => {
this.setState({
filteredActivities: this.filterActivity(event.target.value)
});
};
render() {
const filteredActivities = this.props.filteredActivities;
return(
<div className="notificationsFrame">
<div className="panel">
<Header name={this.props.name} />
<SearchBar onChange={this.handleSearchChange} />
<Content activities={this.state.filteredActivities} />
</div>
</div>
);
}
}
Child class:
class SearchBar extends React.Component {
onChangeHandler = event => {
console.log(event.target.value);
}
render() {
return (
<div className="search-bar" >
<input type="text" onChange={this.onChangeHandler} />
</div>
);
}
}
I want to pass the event.target.value to handleSearchChange. if I put the code of class Searchbar to class App, I can perform a real time searching very good. But I can't put them into two components. I want to make them into two components. Thanks a lot.
Should be as simple as this:-
Child class:
class SearchBar extends React.Component {
onChangeHandler = event => {
this.props.inputChanged(event.target.value);
}
render() {
return (
<div className="search-bar" >
<input type="text" onChange={this.onChangeHandler} />
</div>
);
}
}
Parent class:
handleSearchChange = inputValue => {
this.setState({
filteredActivities: this.filterActivity(inputValue)
});
};
JSX of parent class:
<SearchBar inputChanged={this.handleSearchChange} />
since you're already passing the function's reference as a prop you can access it using this.props.propName and call it.
class SearchBar extends React.Component {
/* onChangeHandler = event => {
console.log(event.target.value);
} */
render() {
const { onChange } = this.props;
return (
<div className="search-bar" >
<input type="text" onChange={onChange} />
</div>
);
}
}
You can try this, as you already took event as a parameter in parent class for handleSearchChange method

ReactJS: This.props.toggleModal is not a function

I'm trying to access a parent method from a child to show a modal on screen and I'm getting the error: This.props.toggleModal is not a function. I'm passing the method down to the child so it can be called and using the correct state (I think). The button does call it's own method which in turn calls the parent. The modal component sits inside App.js.
App.js
class App extends Component {
constructor() {
super()
this.state = {
isOpen: false
}
}
toggleModal = () => {
this.setState({
isOpen: !this.state.isOpen
});
console.log('Open');
}
render() {
return (
<div className="App">
<Modal toggleModal={this.toggleModal} show={this.state.isOpen}
onClose={this.toggleModal}>
Here's some content for the modal
</Modal>
<div className="container">
<Header/>
<main>
<Route path="/users"
children={({ match, ...rest }) => (
<TransitionGroup component={firstChild}>
{match && <UserList {...rest} />}
</TransitionGroup>
)}/>
...
</main>
<Footer />
</div>
</div>
);
}
}
SearchBar.js - (located inside the user page)
class SearchBar extends Component {
constructor(props) {
super(props)
this.state = {
type: this.props.type,
value: ''
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.type !== this.props.type) {
this.setState({ type: nextProps.type });
}
};
handleClick = (e) => {
e.preventDefault();
console.log("Clicked!!!");
this.props.toggleModal();
};
handleChange = e => {
console.log(this.state.type);
this.setState({ value: e.target.value });
};
render () {
const isUser = this.state.type;
let rightContent = null;
if (isUser === "tour" || isUser === "venue") {
rightContent =
<div className="column">
<div className="float-right"><button className="add-new" onClick={this.handleClick}>Add New</button></div>
</div>
} else {
rightContent =
<div className="column">
<div className="float-right">
<div className="results-block">
<b>0</b>/<small>292</small>
</div>
</div>
</div>
}
return (
<div className="row main-search">
<div className="column">
<form action="">
<fieldset>
<label htmlFor="search">
<input type="text"
placeholder="Start typing..."
id="search-box"
onChange={this.handleChange}
value={this.state.value} />
</label>
</fieldset>
</form>
</div>
{rightContent}
</div>
)
}
}
export default SearchBar;
Check IF you getting toggleModal as props in your User Page Compoenent. If yes then pass it explicitly like to SearchBar
<SearchBar toggleModal = {this.props.toggleModal } /> // plus your rest of the props
You have to bind toggleModal with this in constructor so that use it as this.toggleModal.
EX.
this.toggleModal = this.toggleModal.bind(this);
check this ReactJs doc for more info.
https://reactjs.org/docs/handling-events.html

Categories

Resources