How to add input text event handler in react - reactjs

I was enrolled to a react course in udemy and there is an assigment. There the solution was given but it seems like react library files have been updated so the code needs change for state and evenhandler. Here I am posting the code and the answer I found just in case if anyone needs answer.
import React, { Component } from 'react';
import './App.css';
import UserInput from './UserInput/UserInput';
import UserOutput from './UserOutput/UserOutput';
class App extends Component {
state = {
username: 'jkr'
}
usernameChangedHandler = (event) => {
this.setState({username: event.target.value});
}
render() {
return (
<div className="App">
<ol>
<li>Create TWO new components: UserInput and UserOutput</li>
<li>UserInput should hold an input element, UserOutput two paragraphs</li>
<li>Output multiple UserOutput components in the App component (any paragraph texts of your choice)</li>
<li>Pass a username (of your choice) to UserOutput via props and display it there</li>
<li>Add state to the App component (=> the username) and pass the username to the UserOutput component</li>
<li>Add a method to manipulate the state (=> an event-handler method)</li>
<li>Pass the event-handler method reference to the UserInput component and bind it to the input-change event</li>
<li>Ensure that the new input entered by the user overwrites the old username passed to UserOutput</li>
<li>Add two-way-binding to your input (in UserInput) to also display the starting username</li>
<li>Add styling of your choice to your components/ elements in the components - both with inline styles and stylesheets</li>
</ol>
<UserInput
changed={this.usernameChangedHandler}
currentName={this.state.username} />
<UserOutput userName={this.state.username} />
<UserOutput userName={this.state.username} />
<UserOutput userName="Max" />
</div>
);
}
}
export default App;

Here the code for state and event handler needs modification as following
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
username: 'jkr'
};
this.usernameChangedHandler=this.usernameChangedHandler.bind(this);
}
usernameChangedHandler(event) {
this.setState( { username: event.target.value});
}
This would do
Courtesy: https://reactjs.org/docs/forms.html
https://reactjs.org/docs/hooks-state.html

With the functional components the you should be able to handle hooks to manage state. This is how the code would look like.
import {useState} from "react";
const App = () => {
const [userName, setUserName]=useState("");
userNameChangeEventHandler = (e) => {
setUserName(e.target.value);
}
}

Related

How to update render() of a class-base component using onChange method of an input

I have written these to components that are going to search through a JSON file.
The first component consists of two parts 1-it returns elements of JSX representing and 2-it finds matches through the JSON file and fills the data in a Const and passes that to props of the other component which is created to show the results.
The problem is that the render() method of the component does not reload the newly entered data whereas variables work properly and data is passed from the first component to the second one without any problem.
import react from 'react';
import React, { Component } from 'react';
import './index.css';
import ResultP from './ResultP';
import userData from './userData';
class SearchP extends Component {
constructor(props){
super(props);
this.state = {
SearchValue : "",
MatchingItems : []
}
}
handleChange = (word)=> {
const match = word.target.value;
const Matches = userData.filter((users) => {return users.name.includes(match);});
this.setState({MatchingItems : Matches})
console.log(this.state.MatchingItems);
};
render() {
return (
<div>
<form className="my-form" >
<input type="search" onChange={this.handleChange}
placeholder="Write your keyword here" />
</form>
<ResultP SearchString = {this.state.MatchingItems} />
</div>
)
}
}
export default SearchP;
this one was the first component.
Below is the second one
import React, { Component } from 'react';
class ResultP extends Component {
constructor(props){
super (props);
this.state = {
SearchString : this.props.SearchString,
}
}
render() {
return (
<section className="main-section">
{this.state.SearchString}
</section>
)
}
}
export default ResultP;
The constructor logic only executes once when the component is initially rendered, so when you initialize a state variable in the constructor with a prop value, that state variable won't update if the prop value changes. You'll need to add the getDerivedStateFromProps function in your child component: https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops. Here's an example: https://www.tutorialspoint.com/reactjs-getderivedstatefromprops-method

React - How to change code written in the old way when using a new approach?

I was looking for steps on the internet to refactor previous applications for a new approach, but I did not find a satisfactory answer...
I know the previous approach for creating applications in ReactJS which uses the Component and render() function for example, but I can see that it is currently different because:
npx create-react-app app-name
is now generating a different template.
For example, previously it was imported:
import React, {Component} from 'react';
and now only:
import React from 'react';
I am asking for tips or simple advice on what I should change so that the old code works without importing the Component.
Has the method of using functions for communication via AJAX (e.g. loadPerson) changed as well?
For example here is some not working example of ./src/PersonDetail.js:
import React from 'react';
import { DetailList } from './DetailList';
import { loadPerson } from './requests';
export class PersonDetail {
constructor(props) {
super(props);
this.state = {person: null};
}
async componentDidMount() {
const {personId} = this.props.match.params;
const person = await loadPerson(personId);
this.setState({person});
}
render() {
const {person} = this.state;
if (!person) {
return null;
}
return (
<div>
<h1 className="title">{person.name}</h1>
<div className="box">{person.description}</div>
<h5 className="title is-5">Details at {person.name}</h5>
<DetailList details={person.details} />
</div>
);
}
}
Thank you in advance.
With the introduction of hooks some core concepts begin to change. Before React 16.8 we used to have a rule of thumb to decide rather a component should be based on class or function:
If the component should hold state then it should be class based. If it doesn't have state (stateless) then it could be a functional component
This used to be true cause there wasn't a way to implement stateful logic inside functional components. Now hooks allow you to implement state in functional components.
The boilerplate generated by create-react-app doesn't import Component from react anymore cause only class based components need to extends from Component and App is now a functional component.
Nothing changed really it's just another way to write your components.
Just import like it used to be:
export class PersonDetail extends React.Component
or give hooks a chance and turn your component into a functional component:
import React, { useState, useEffect } from 'react';
import { DetailList } from './DetailList';
import { loadPerson } from './requests';
const PersonDetail = ({ personID }) => {
const [person, setPerson] = useState(false)
useEffect(() => {
const person = await loadPerson(personId)
setPerson(person)
}, [personID])
return !person ? null : (
<div>
<h1 className="title">{person.name}</h1>
<div className="box">{person.description}</div>
<h5 className="title is-5">Details at {person.name}</h5>
<DetailList details={person.details} />
</div>
)
}
To add to other comments...
You can have class-based components side by side to functional components, there is no inherent need to re-write any old class-based components you have written.
Introducing Hooks
No Breaking Changes
Before we continue, note that Hooks are:
Completely opt-in. You can try Hooks in a few components without rewriting any existing code. But you don’t have to learn or use Hooks right now if you don’t want to.
100% backwards-compatible. Hooks don’t contain any breaking changes.
Available now. Hooks are now available with the release of v16.8.0.
I am assuming the "new approach" you are talking about are React Hooks.
import * as React from "react";
import { DetailList } from "./DetailList";
import { loadPerson } from "./requests";
const PersonDetail = props => {
const [person, setPerson] = React.useState(null);
React.useEffect(() => {
(async () => {
const { personId } = props.match.params;
setPerson(await loadPerson(personId));
})();
}, []);
if (!person) {
return null;
}
return (
<div>
<h1 className="title">{person.name}</h1>
<div className="box">{person.description}</div>
<h5 className="title is-5">Details at {person.name}</h5>
<DetailList details={person.details} />
</div>
);
};
export { PersonDetail };
The change you noticed is that create-react-app now creates functional components. This means your components are no longer classes but just functions.
You can still import Component and export a class that extends Component.
You don't need to write your class as a function but to write the example class as a functional component you can do the following:
import React, { useEffect, useState } from 'react';
import { DetailList } from './DetailList';
import { loadPerson } from './requests';
export default props => {
const { personId } = props.match.params;
const [person, setPerson] = useState(null);
useEffect(() => {
loadPerson(personId).then(person => setPerson(person));
}, [personId]);
if (!person) {
return null;
}
return (
<div>
<h1 className="title">{person.name}</h1>
<div className="box">{person.description}</div>
<h5 className="title is-5">
Details at {person.name}
</h5>
<DetailList details={person.details} />
</div>
);
};
You can read more about react hooks here
With this import,
import React from 'react';
You can extend Component without importing like,
export class PersonDetail extends React.Component{ ...}
Update
Hooks are newly added in React 16.8 and it is not recommended that we should change our exsiting code with Hooks. We can still have our exsiting class-based approach which extends Component or React.Component.
Hooks gives us the capability of maintaining state of the component as well as it gives a space to write React lifecycle methods.
For example, state in class-based component
state = {
stateVariable : "stateValue"
}
In new approach, it is equivalent to
const [stateVariable, setStateVariable] = useState("stateValue")
And for the lifecycle methods we have useEffect.
If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.
useEffect(() => {
console.log(stateVariable);
});
of-course we need to import useState and useEffect from react package,
import React, {useState, useEffect} from 'react'
Finally, the class-based component
import React from 'react'
class MyComponent extends React.Component{
state={
stateVariable : "stateValue"
}
componentDidMount(){
console.log(this.state.stateVariable)
}
render(){
return(
<div> {this.state.stateVariable} </div>
)
}
}
can be converted to functional component like,
import React, {useState, useEffect} from 'react'
const MyComponent = (props) => {
const [stateVariable, setStateVariable] = useState("stateValue")
useEffect(()=>{
console.log(stateVariable)
})
return(
<div> {stateVariable} </div>
)
}
Note: We don't have access to this in functional component because we don't have class anymore.
If you want to use class components, you have to make them extend React.Component.
There is another way to create components now, which is the functional way. Meaning that a component can be a simple JavaScript function returning an element (usually, some JSX). In this case, you don't need to import React.Component anymore.
Class component:
import React, { Component } from "react"
class PersonDetail extends Component {
render() {
...
}
...
}
Functional component (this is the way React and the community is now pushing for):
import React from "react"
const PersonDetail = () => {
return (<Your JSX code>)
}

How to make an API request In React on button click

I'm trying to build a random quote generator that loads A quote on componentDidMount with an axios api request , then loads new quotes on button click.
This is for A freecodecamp project. I have tried making the call again on button click, then adding the new response to state, but it will not work at all.
import React, { Component } from 'react'
import Button from './Button';
import axios from 'axios'
class QuoteBox extends Component{
constructor(props){
super(props)
this.state = {
quotes: []
}
}
componentDidMount(){
axios.get('http://quotesondesign.com/wp-json/posts?
filter[orderby]=rand&filter[posts_per_page]=1')
.then(res=> this.setState({quotes: res.data[0]}))
}
getNext = (ev) =>{
ev.preventDefault()
axios.get('http://quotesondesign.com/wp-json/posts?
filter[orderby]=rand&filter[posts_per_page]=2')
.then(res=> this.setState({quotes:[...this.state,res.data[0]]}))
}
render(){
const {content,title} = this.state.quotes
const filteredContent = String(content).replace(/(<\w>)|(<\/\w>)|
(&#\d{4})/gm, "").replace(/(;)/g,"'")
console.log(content)
return(
<React.Fragment>
<h2>A little inspiration for the day</h2>
<div className='outerQuoteBox'>
<div className='innerQuoteBox'>
<p>{filteredContent}</p><br/><br/>{title}
</div>
<Button getNext={this.getNext} />
</div>
</React.Fragment>)
}
}
export default QuoteBox
And this is my button component
import React, { Component } from 'react'
export class Button extends Component {
render() {
return (
<React.Fragment>
<button onClick={this.props.getNext} className='nextBtn'
type='button'>Next</button>
</React.Fragment>
)
}
}
export default Button
When I click the button, it seems like the request isn't going through at all. If i check State in the dev tools, only the first quote from componentDidMount is in the array. I don't understand where my mistake is.
Edit: I had used the wrong prop reference, so it wasn't making the call. I fixed this and it does make the call now, and it brings in one new quote, but that's it. And it doesn't add the new one to state, it just replaces it with the one new one. and that's all it will do. The api instructions say the end point i'm using should return a new random quote, but it does not.
It looks like you're referencing the wrong prop on the button.
Change getQuote to getNext and it should work...
import React, { Component } from 'react'
export class Button extends Component {
render() {
return (
<React.Fragment>
<button onClick={this.props.getNext} className='nextBtn'
type='button'>Next</button>
</React.Fragment>
)
}
}
export default Button

Better practice for identifying what part of state to change when having one event handler for multiple dynamically created fields?

In React, callback functions are often used to send a signal from a child to a parent that something has happened.
I am building an application where the user is able to dynamically create more fields, and all change events for all fields are served by the same callback function. See my code below. As you can see I am using event, name, and key to know what part of my state to change.
Editable component passes name to parent
import React, { Component } from "react";
export default class Editable extends Component {
constructor(props) {
super(props);
this.onBlur = this.onBlur.bind(this)
}
onBlur(event) {
this.props.handleBlur(event, this.props.name)
}
render() {
return (
<div>
<div
contentEditable={true}
onBlur={this.onBlur}
dangerouslySetInnerHTML={{ __html: this.props.html }}
/>
</div>
);
}
}
Subject component passes name and id to parent
import React, { Component } from "react";
import Editable from "./Editable";
export default class Subject extends Component {
constructor(props) {
super(props);
this.handleBlur = this.handleBlur.bind(this);
}
handleBlur(event, name) {
this.props.handleBlur(event, name, this.props.skillId);
}
render() {
return (
<div>
<p>subject</p>
<Editable
handleBlur={this.handleBlur}
html={this.props.subject}
name="subject"
/>
</div>
);
}
}
The callback in the parent has the callback it needs to update the correct skill[property] in the state.
import Skill from "./Skill";
export default class Skills extends Component {
constructor(props) {
this.handleBlur = this.handleBlur.bind(this);
}
handleBlur(event, name, key) {
// Shange the state
}
render() {
var skills = [];
this.props.skills.map(element => {
skills.push(
<Skill
key={element.id}
skillId={element.id}
subject={element.subject}
ability={element.ability}
handleBlur={this.props.handleBlur}
/>
);
});
return <div>{skills}</div>;
}
}
What is a better practice for identifying what part of state to change when having one event handler for multiple dynamically created fields?

React + Redux: Separating the presentation from the data

I am building a weather app with React & Redux. I've decided to venture into uncharted waters as a noob to React & Redux. I'm splitting things up into presentational components and their respective container that will handle the data. I'm having some problems wrapping my head around this though. It might come down to how I'm trying to do it I'm just really unsure.
Right now I have SearchBar, CurrentWeather, & Forecast components and an AppContainer that I'm trying to integrate those components into. I have the SearchBar component integrated into the AppContainer so far and it is working with no problems. Here is where I am getting confused. So I have provided the needed actions and components to the container and the container has been connected so when the user does a search the api call will be made and the state will update through the reducers.
That data should be available through mapStateToProps now correct?
How can I go about using that data after the user has performed the action but have it not be used upon the initial render? If AppContainer is rendering these three components I will obviously be passing props to them so they render and function as they are expected to. I'm thinking this is where a lifecycle could be used I'm just unsure of which or how to use them. My code for the AppContainer, SearcBar, & CurrentWeather are below. CurrentWeather & Forecast are nearly identical (only providing different data from different endpoints for the api) so I did not provide it. I also didn't provide the actions or reducers because I know they work fine before I decided to attempt this refactor. Maybe I need more than one container to pull this off? Any advice or direction would be greatly appreciated, thanks all and have a good night.
** Do have a side question: on _weatherSearch I have event.preventDefault(); because the SearchBar is a form element. Do I even need to provide this? If event is not what is being passed but the term I think no. The event is being used as seen below in the form element of SearchBar:
onSubmit={event => getWeather(event.target.value)}
App Container:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchCurrentWeather, fetchForecast } from '../actions/actions';
import SearchBar from '../components/SearchBar';
import CurrentWeather from '../components/CurrentWeather';
class AppContainer extends Component {
_weatherSearch(term) {
event.preventDefault();
// Here is where we go to fetch weather data.
this.props.fetchCurrentWeather(term);
this.props.fetchForecast(term);
}
render() {
const getWeather = term => {this._weatherSearch(term);};
return (
<div className="application">
<SearchBar getWeather={getWeather}/>
<CurrentWeather />
</div>
);
}
}
const mapStateToProps = ({ current, forecast }) => {
return {
current,
forecast
}
}
export default connect(mapStateToProps,
{ fetchCurrentWeather, fetchForecast })(AppContainer);
SearchBar:
import React from 'react';
const SearchBar = ({ getWeather }) => {
return(
<form className='input-group' onSubmit={event => getWeather(event.target.value)}>
<input
className='form-control'
placeholder='Search a US City' />
<span className='input-group-btn'>
<button className='btn btn-secondary' type='submit'>Submit</button>
</span>
</form>
);
}
export default SearchBar;
CurrentWeather: *NOTE: I have not removed any of the logic or data processing from CurrentWeather yet so it has not been refactored to a presentational only component yet.
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {unitConverter} from '../conversions/conversions_2.0';
export class CurrentWeather extends Component {
_renderCurrentWeather(cityData) {
const name = cityData.name;
const {temp, pressure, humidity} = cityData.main;
const {speed, deg} = cityData.wind;
const {sunrise, sunset} = cityData.sys;
return (
<tr key={name}>
<td>{unitConverter.toFarenheit(temp)} F</td>
<td>{unitConverter.toInchesHG(pressure)}"</td>
<td>{humidity}%</td>
<td>{unitConverter.toMPH(speed)}mph {unitConverter.toCardinal(deg)}</td>
</tr>
);
}
render() {
let currentWeatherData = [];
if (this.props.current) {
currentWeatherData = this.props.current.map(this._renderCurrentWeather);
}
return (
<table className="table table-reflow">
<thead>
<tr>
<th>Temperature</th>
<th>Pressure</th>
<th>Humidity</th>
<th>Wind</th>
</tr>
</thead>
<tbody>
{currentWeatherData}
</tbody>
</table>
);
}
}
function mapStateToProps({current}) {
return {current};
}
export default connect(mapStateToProps)(CurrentWeather);
Your render function is very dynamic. You can omit anything you like:
class AppContainer extends Component {
_weatherSearch(term) {
// event.preventDefault(); We can't do this because we don't have an event here...
this.props.fetchCurrentWeather(term);
this.props.fetchForecast(term);
}
render() {
const getWeather = term => { this._weatherSearch(term); };
return (
<div className="application">
<SearchBar getWeather={getWeather}/>
{ Boolean(this.props.current) && <CurrentWeather /> }
</div>
);
}
}
const mapStateToProps = ({ current }) => ({ current });
export default connect(mapStateToProps,
{ fetchCurrentWeather, fetchForecast })(AppContainer);
This is how you deal with missing data. You just either show nothing, or a message to search first, or if it's loading,you can show a spinner or throbber.
The technique used above to hide CurrentWeather is to pass a Boolean to React if we're wanting to hide the component. React ignores true, false, null and undefined.
Note that it's a good idea to only ever pass data in mapStateToProps that you'll actually be using inside the component itself. In your code you're passing current and forecast but you don't use them.
Redux will rerender when any of the mapStateToProps, mapDispatchToProps or props data changes. By returning data you'll never use you instruct Redux to rerender when it's not necessary.
I'm a react-redux noob myself :-) and I've come across similar issues.
As far as I can tell, the container/presentational separation you've made looks good, but you can go even a step further and separate the container's fetching and mounting.
The solution I'm referring to is what people variously call "higher-order components" and "wrapper components": (the code below isn't tested - it's just for illustration purposes)
import {connect} from blah;
const AppWrap = (Wrapped) => {
class AppWrapper extends Component {
constructor(props) {
super(props);
this.state = {foo: false};
}
componentWillMount() {
this.props.actions.fooAction()
.then(() => this.setState({foo: false}));
}
render() {
return (<Wrapped {...this.props} foo={this.state.foo}/>);
}
}
function mapState(state) { blah }
function mapDispatch(dispatch) { blah }
return connect(mapState, mapDispatch)(AppWrapper);
}
export default AppWrap;
Notice the = (Wrapped) => { part at the top. That is what's doing the actual "wrapping", and the argument can be named anything so long as you refer to it in the render hook.
Now inside your AppContainer, you get a this.props.foo which acts as a flag telling you that fooAction() has completed, and you can use it to render your presentational components accordingly. Until fooAction completes, you can be sure that the foo passed into AppContainer will be false.
To put what I just said into code, your AppContainer might look something like this:
import AppWrapper from './AppWrapper';
class AppContainer extends Component {
constructor(props) {
super(props);
}
render() {
return (!this.props.foo) ? <div>bar</div> : (
<div blah>
<SearchBar blah/>
<CurrentWeather blah/>
</div>
);
}
}
export default AppWrapper(AppContainer);
The benefit of using a wrapper component like this is that
you can take more control over how and when exactly the data gets rendered
account for "loading" mechanisms and logic
avoid quirky problems like having to make dispatches within componentWillMount hooks and having to deal with the consequences.
Take a look at this blog post for more information about HoCs: https://medium.com/#dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750

Resources