Converting a stateful component to stateless - javascript

i'm pretty new to react and redux and i'm having an issue here. It's mandatory to use only stateless components with containers whenever state handing is required. These two components are:
import React from 'react';
import DatePicker from '../DatePicker';
class DayInput extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
dateValue: new Date(),
activeDateWidget: false,
};
}
changeDate(date) {
this.setState({
dateValue: date,
});
}
changeActiveDateWidget(e) {
e.stopPropagation();
this.setState({
activeDateWidget: !this.state.activeDateWidget,
});
}
render() {
const { input, meta } = this.props;
const { dateValue, activeDateWidget } = this.state;
return (
<div>
<input
{...input}
className="form-control"
type="text"
value={dateValue}
onClick={this.changeActiveDateWidget}
// onBlur={this.changeActiveDateWidget}
/>
{activeDateWidget ? (
<div>
<DatePicker
changeActiveDateWidget={this.changeActiveDateWidget}
changeDate={this.changeDate}
dateValue={dateValue}
/>
</div>
) : (
<div />
)}
</div>
);
}
}
export default DayInput;
import React from 'react';
import 'react-day-picker/lib/style.css';
import DayPicker, { DateUtils } from 'react-day-picker';
class DatePicker extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
selectedDay: new Date(),
};
}
componentDidMount() {
if (this.input) {
this.input.focus();
}
}
handleDayClick(e, day, { disabled }) {
e.stopPropagation();
if (disabled) {
return;
}
this.setState({ selectedDay: day }, () => {
this.props.changeDate(day);
this.props.changeActiveDateWidget();
});
}
focusThisComponent(e) {
if (e) {
this.input = e;
}
}
render() {
const { changeActiveDateWidget } = this.props;
const { selectedDay } = this.state;
return (
<div
ref={this.focusThisComponent}
tabIndex="1"
>
<DayPicker
id="THISTHING"
initialMonth={selectedDay}
selectedDays={day => DateUtils.isSameDay(selectedDay, day)}
onDayClick={this.handleDayClick}
/>
</div>
);
}
}
export default DatePicker;
As you can see the first component is wrapped inside the second component. I tried to convert the first component myself like this:
const DayInput = props => {
<input
{...props.input}
type="text"
value= {new Date()}
onClick={()=>??}
/>
}
but as you can see i dont know how to handle the onclick event. Can someone help me to achieve this?

To turn your component in a stateless component, you must pass everything as properties of the component.
This will be your DayInput splitted into 2 components :
const DayInputShow = props => {
return (<input
{...props.input}
type="text"
value= {props.value}
onClick={(event)=>props.onClick()}
/>);
};
const DayInputEdit = props => {
return (<DatePicker
changeActiveDateWidget={props.changeActiveDateWidget}
changeDate={props.onChange}
dateValue={props.value}
/>);
};
DayInputShow.propTypes = {
value: PropTypes.date,
onClick: PropTypes.func,
}
DayInputEdit.propTypes = {
value: PropTypes.date,
onChange: PropTypes.func,
}
And this will be the root component (uncomplete and still statefull) :
class DatePicker extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
selectedDay: new Date(),
};
}
componentDidMount() {
if (this.input) {
this.input.focus();
}
}
handleDayClick(e, day, { disabled }) {
e.stopPropagation();
if (disabled) {
return;
}
this.setState({ selectedDay: day }, () => {
this.props.changeDate(day);
this.props.changeActiveDateWidget();
});
}
focusThisComponent(e) {
if (e) {
this.input = e;
}
}
render() {
const { changeActiveDateWidget } = this.props;
const { selectedDay } = this.state;
let dayPicker;
if (this.input) {
dayPicker = <DayPickerEdit
value={this.state.selectedDay}
onChange={(value) => {this.setState({selectedDay: value})}}
selectedDays={day => DateUtils.isSameDay(selectedDay, day)}
onDayClick={this.handleDayClick}
/>
} else {
dayPicker = <DayPickerShow
value={this.state.selectedDay}
ref={(input) => { this.inputRef = input; }} />
onClick={() => {this.focusThisComponent(this.inputRef )}}
/>
}
return (
<div
ref={this.focusThisComponent}
tabIndex="1"
>
{dayPicker }
</div>
);
}
}
export default DatePicker;

Related

Different class component and func component in handle event react

Can anyone help me explain it, because when use functional it work, but not when use class component
Different class component and func component in handle event react.
fyi it take input text search on child and at parent doing handle event and other
child use functional
import React from "react";
const Header = ({searchTitle,onSearch}) => {
return (
<div>
<header>
<h1>Notes</h1>
<div className="search">
<input
type="text"
placeholder="Search..."
value={searchTitle}
onChange={onSearch}
></input>
</div>
</header>
</div>
);
};
export default Header;
child use class component
import React, { Component } from "react";
export class HeaderPage extends Component {
render() {
const searchTitle = this.props.searchTitle;
const onSearch = this.props.onSearchHandler;
return (
<header>
<h1>Notes</h1>
<div className="search">
<input
type="text"
placeholder="Search..."
value={searchTitle}
onChange={onSearch}
></input>
</div>
</header>
);
}
}
export default HeaderPage;
parent class componet
import React, { Component } from "react";
import HeaderPage from "../organisms/HeaderPage";
import MainPage from "../organisms/MainPage";
import FooterPage from "../organisms/FooterPage";
import { getInitialData } from "../../utils/data";
import Header from "../organisms/Header";
export class PageNote extends Component {
constructor(props) {
super(props);
this.state = {
dataNotes: getInitialData(),
dataNotesFiltered: [],
searchTitle: "",
};
//binding
this.onAddNoteHandler = this.onAddNoteHandler.bind(this);
this.onDeleteHandler = this.onDeleteHandler.bind(this);
this.onArchivedHandler = this.onArchivedHandler.bind(this);
this.onSearchHandler = this.onSearchHandler.bind(this);
}
onAddNoteHandler({ title, body }) {
this.setState((prevState) => {
return {
dataNotes: [
...prevState.dataNotes,
{
id: +new Date(),
title,
body,
archived: false,
createdAt: new Date().toLocaleDateString(),
},
],
};
});
}
onDeleteHandler(id) {
const dataNotes = this.state.dataNotes.filter(
(dataNote) => dataNote.id !== id
);
this.setState({ dataNotes });
}
onArchivedHandler(id) {
const dataNotes = this.state.dataNotes.map((note) => {
if (note.id === id) {
return { ...note, archived: !note.archived };
} else {
return note;
}
});
this.setState({ dataNotes });
}
onSearchHandler(event) {
this.setState(() => {
return {
searchTitle: event.target.value,
};
});
}
render() {
console.log(this.state.dataNotes);
console.log(`search ${this.state.searchTitle}`);
const dataNotes = this.state.dataNotes.filter((note) =>
note.title.toLowerCase().includes(this.state.searchTitle.toLowerCase())
);
return (
<>
<HeaderPage
searchTitle={this.state.searchTitle}
onSearch={this.onSearchHandler}
></HeaderPage>
<Header
searchTitle={this.state.searchTitle}
onSearch={this.onSearchHandler}
></Header>
<MainPage
dataNotes={dataNotes}
addNote={this.onAddNoteHandler}
onDelete={this.onDeleteHandler}
onArchive={this.onArchivedHandler}
></MainPage>
<FooterPage></FooterPage>
</>
);
}
}
export default PageNote;

ReactJS how to access content output from a component?

I'm currently working on a project that uses QuillJS for a rich text editor. I need to post the rich text content to my backend but I'm not sure how to access the QuillJS output.
In RichTextEditor.js
import React, { Component } from "react";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
class RichTextEditor extends Component {
constructor(props) {
super(props);
// this.formats = formats;
this.state = { text: "" }; // You can also pass a Quill Delta here
this.handleChange = this.handleChange.bind(this);
}
handleChange(value) {
this.setState({ text: value });
const text = this.state;
console.log(text);
}
render() {
return (
<ReactQuill
value={this.state.text}
onChange={this.handleChange}
formats={this.formats}
modules={this.modules}
/>
);
}
}
export default RichTextEditor;
The console.log(text) basically just outputs the content of the rich text editor. Something like this "<p><em>aasdasdasd</em><strong><em>asdasdasdasd</em></strong></p>"
In Post.js
import React, { Component } from "react";
import RichTextEditor from "./RichTextEditor.js";
import "../../css/Post.css";
class Post extends Component {
constructor(props) {
super(props);
this.state = {
question: "",
};
}
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
console.log(this.state);
};
handleSubmit = (e) => {
e.preventDefault();
const { question } = this.state;
console.log("Question");
console.log(question);
render() {
const { question } = this.state;
return (
<div className="post">
<div className="post__container">
<form onSubmit={this.handleSubmit}>
<div className="post__richTextEditor">
<RichTextEditor value={question} onChange={this.onChange} name="question" />
</div>
</form>
</div>
</div>
);
}
}
export default Post;
I'm trying to update the state of the question but it doesn't seem to be updating. console.log(question) only outputs a single string.
How can I access the same string output from RichTextEditor.js?
Your RichTextEditor component should not handle change, it should only receive props from higher component:
class RichTextEditor extends Component {
constructor(props) {
super(props);
}
render() {
return (
<ReactQuill
value={this.props.value}
onChange={this.props.onChange}
formats={this.formats}
modules={this.modules}
/>
);
}
}
export default RichTextEditor;
Then your Post component pass value and onChange props to RichTextEditor:
class Post extends Component {
constructor(props) {
super(props);
this.state = {
question: "",
};
this.onChange = this.onChange.bind(this);
}
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
console.log(this.state);
};
handleSubmit = (e) => {
e.preventDefault();
const { question } = this.state;
console.log("Question");
console.log(question);
render() {
const { question } = this.state;
return (
<div className="post">
<div className="post__container">
<form onSubmit={this.handleSubmit}>
<div className="post__richTextEditor">
<RichTextEditor value={question} onChange={this.onChange} name="question" />
</div>
</form>
</div>
</div>
);
}
}
in RichTextEditor.js
handleChange(value) {
this.setState({ text: value });
const text = this.state;
console.log(text);
props.onChange(text); // passing the inner State to parent as argument to onChange handler
}
Now in Post.js
onChange = (newStateString) => {
//this.setState({ [e.target.name]: e.target.value });
console.log(newStateString); // you should get the string here
};

TypeError: this.props.* is not a function

I get this error in my code
TypeError: this.props.* is not a function
and i cannot figure it out what is wrong with it.
I have tried different approaches butt still gives me this error even if I "binded" the function to "this".
SingleCurPlaylist.js
import React from "react";
import "./SingleCurPlaylist.css";
class SingleCurPlaylist extends React.Component {
constructor(props) {
super(props);
this.showList = this.showList.bind(this);
}
showList() {
this.props.showList(this.props.id);
}
render() {
return (
<div className="Single-list" onClick={this.showList}>
<h4>{this.props.name}</h4>
</div>
);
}
}
export default SingleCurPlaylist;
PlayistSpotify.js
import React from "react";
import SingleCurPlaylist from "../SingleCurPlaylist/SingleCurPlaylist";
import "./PlaylistSpotify.css";
class PlaylistSpotify extends React.Component {
constructor(props) {
super(props);
this.renderList = this.renderList.bind(this);
}
renderList() {
this.props.getLocalPlaylists();
}
render() {
return (
<div className="PlaylistSpotify">
<h2>Local Playlists</h2>
<button className="Playlist-get" onClick={this.renderList}>
Get your local playlist
</button>
{this.props.playlistLists.map((singlePlay) => (
<SingleCurPlaylist
showList={this.props.showList}
id={singlePlay.id}
key={singlePlay.id}
name={singlePlay.name}
/>
))}
</div>
);
}
}
export default PlaylistSpotify;
This is App.js, where once the error is fixed "showList" is supposed to receive the playlist id
import React from "react";
import SearchBar from "../SearchBar/SearchBar";
import SearchResults from "../SearchResults/SearchResults";
import Playlist from "../Playlist/Playlist";
import Spotify from "../../util/Spotify";
import PlaylistSpotify from "../PlaylistSpotify/PlaylistSpotify";
import "./App.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchInput: "",
searchResults: [],
playlistName: "New Playlist",
playlistTracks: [],
localPlaylists: [],
};
this.addTrack = this.addTrack.bind(this);
this.removeTrack = this.removeTrack.bind(this);
this.updatePlaylistName = this.updatePlaylistName.bind(this);
this.savePlaylist = this.savePlaylist.bind(this);
this.search = this.search.bind(this);
this.getLocalPlaylists = this.getLocalPlaylists.bind(this);
}
addTrack(track) {
let newPlaylist = this.state.playlistTracks;
if (newPlaylist.find((playTrack) => playTrack.id === track.id)) {
return;
} else {
newPlaylist.push(track);
}
this.setState({ playlistTracks: newPlaylist });
}
showList(playlist) {
console.log(playlist);
}
removeTrack(track) {
let playlist = this.state.playlistTracks;
let newPlaylist = playlist.filter((playTrack) => playTrack.id !== track.id);
this.setState({ playlistTracks: newPlaylist });
}
updatePlaylistName(name) {
this.setState({ playlistName: name });
}
savePlaylist() {
const trackUris = this.state.playlistTracks.map((track) => track.uri);
Spotify.savePlaylist(this.state.playlistName, trackUris).then(() => {
this.setState({
playlistName: "New Playlist",
playlistTracks: [],
});
});
}
// keep search after url updated
componentDidMount() {
Spotify.getAccessToken();
}
getLocalPlaylists() {
Spotify.getPlaylist().then((playlistLists) => {
let newList = playlistLists.items.map((playlist) => {
return {
name: playlist.name,
id: playlist.id,
};
});
this.setState({ localPlaylists: newList });
});
}
search(term) {
Spotify.search(term).then((searchResults) => {
// filters results in order not to show tracks already in the playlist
searchResults = searchResults.filter((track) => {
return this.state.playlistTracks.every((el) => el.uri !== track.uri);
});
this.setState({ searchResults: searchResults, searchInput: term });
});
}
render() {
return (
<div>
<h1>
Ja<span className="highlight">mmm</span>ing
</h1>
<div className="App">
{<SearchBar onSearch={this.search} />}
<div className="App-playlist">
{
<SearchResults
searchResults={this.state.searchResults}
onAdd={this.addTrack}
/>
}
{
<Playlist
playlistName={this.state.playlistName}
playlistTracks={this.state.playlistTracks}
onRemove={this.removeTrack}
onNameChange={this.updatePlaylistName}
onSave={this.savePlaylist}
showList={this.showList}
/>
}
{
<PlaylistSpotify
getLocalPlaylists={this.getLocalPlaylists}
playlistLists={this.state.localPlaylists}
/>
}
</div>
</div>
</div>
);
}
}
export default App;
Thanks for your help!
You don't pass showList={this.showList} to PlaylistSpotify, so it is undefined when passing to SingleCurPlaylist and on.
<PlaylistSpotify
getLocalPlaylists={this.getLocalPlaylists}
playlistLists={this.state.localPlaylists}
showList={this.showList}
/>
in PlayistSpotify.js file, in the playlistLists loop, this.props.showList is unedfined:
{this.props.playlistLists.map((singlePlay) => (
<SingleCurPlaylist
showList={this.props.showList} // this is undefined
id={singlePlay.id}
key={singlePlay.id}
name={singlePlay.name}
/>
))}
because you're not passing it from App.js:
{<PlaylistSpotify
getLocalPlaylists={this.getLocalPlaylists}
playlistLists={this.state.localPlaylists}
//showList is not passed
/>
}

React JS : grandparent component's setState method doesn't update state of a grandchild input field onChange event click

In the same code, I was able to the get the grandparent component's setState method to update accordingly for an onClick event from the grandchild component, however, for the onChange event, it is failing. I am not getting any errors.
class GrandChild extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
changeNumber=()=> {
this.props.changeNumber();//call child method
}
handleChange(e) {
this.props.onChange(e.target.value);
}
render() {
const data = this.props.data;
return(
<div>
<h1>The number is {this.props.number}</h1>
<input type="text" value = {data} onChange={this.handleChange} />
<button onClick={this.changeNumber}>Increase number by 1</button>
</div>
)
}
}
class Child extends React.Component {
render() {
return(
<div>
<GrandChild number={this.props.number} changeNumber={this.props.changeNumber} value={this.props.data} onChange={this.props.handleChange}/>
</div>
)
}
}
class App extends React.Component {
constructor() {
super()
this.state = {
number: 1,
data: ""
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(data) {
this.setState({data:this.state.data});
console.log(data);
}
changeNumber=()=>{
this.setState((prevState)=>{
console.log(prevState,this.state.data);
return {
number : prevState.number + 1
}
});
}
render() {
const data = this.state.data;
const input = data;
return (
<Child number={this.state.number}
changeNumber = {this.changeNumber}
data={input}
onChange = {this.handleChange}
/>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
Console Result:
Object {
data: "",
number: 1
} ""
result screenshot:
console.log result
see code pen for live code:
https://codepen.io/codehorse/pen/yLyEwBw?editors=0011
Your improved code with live demo https://codesandbox.io/s/laughing-sky-kk97b
What need to change <GrandChild number={this.props.number} changeNumber={this.props.changeNumber} value={this.props.data} onChange={this.props.onChange}/>
Complete Code
class GrandChild extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
changeNumber = () => {
this.props.changeNumber(); //call child method
};
handleChange(e) {
this.props.onChange(e.target.value);
}
render() {
const data = this.props.data;
return (
<div>
<h1>The number is {this.props.number}</h1>
<input type="text" value={data} onChange={this.props.onChange} />
<button onClick={this.changeNumber}>Increase number by 1</button>
</div>
);
}
}
class Child extends React.Component {
render() {
return (
<div>
<GrandChild
number={this.props.number}
changeNumber={this.props.changeNumber}
value={this.props.data}
onChange={this.props.onChange}
/>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
number: 1,
data: ""
};
}
handleChange = e => {
this.setState({ data: e.target.value });
console.log(e.target.value);
};
changeNumber = () => {
this.setState(prevState => {
console.log(prevState, this.state.data);
return {
number: prevState.number + 1
};
});
};
render() {
const data = this.state.data;
const input = data;
return (
<Child
number={this.state.number}
changeNumber={this.changeNumber}
data={input}
onChange={this.handleChange}
/>
);
}
}
export default App;

How to Enter Text By User Input in React JS

I am trying to get the user to input data for my TODO App instead of picking up data from a predefined array.
Below is my Parent component
import React from "react"
import TodoItem from "./TodoItem"
import todosData from "./todosData"
class App extends React.Component {
constructor() {
super()
this.state = {
name: "",
todos: todosData
}
this.handleChange = this.handleChange.bind(this)
this.nameEnter = this.nameEnter.bind(this)
}
handleChange(id) {
this.setState(prevState => {
const updatedTodos = prevState.todos.map(item => {
if (item.id === id){
return {
...item,
completed: !item.completed
}
}
return item
})
return {
todos: updatedTodos
}
})
}
nameEnter(){
var name = this.name
console.log(name)
}
render() {
const todoItems = this.state.todos.map(item => <TodoItem key={item.id} item={item}
handleChange = {this.handleChange} nameEnter= {this.nameEnter}/>)
return (
<div className="todo-list">
{todoItems}
</div>
)
}
}
export default App
This one is the child component where I have added input fields
import React from "react"
function TodoItem(props) {
return (
<div className="todo-item">
<input
type = "checkbox"
checked = {props.item.completed}
onChange = { () => props.handleChange(props.item.id) }
/>
<input type = "text" name= "name" />
<p>{props.item.text}</p>
</div>
)
}
export default TodoItem
This is how my page looks. Instead of predefined text example:"GROCERRY SHOPPING", I want the user to enter text and it should be in place of that.
Ok so here it is
<input onChange={(e)=>handleChange(e)}/>
//react hooks
const [values,setValues] = useState({inputValue: 'predefined'});
handleChange = (e)=>{
setValues({...values,[e.target.name]: e.target.value})
}
//classes
handleChange = (e)=>{
this.setState({[e.target.name]:e.target.value});
}
this generic and can apply to a number of inputs, not just one
import React from "react"
class Form extends React.Component {
constructor() {
super()
this.state = {
name: ""
}
}
onChange = (e) => {
this.setState({
[e.target.name]: e.target.value
})
}
render() {
const {name} = this.state
return (
<input type="text" name="name" value={name} onChange={this.onChange}/>
)
}
}
export default Form
changeInputVal (ev, index) {
ev.persist();
const newArr = this.state.inputArrVal;
newArr[index]=ev.target.value;
this.setState({inputArrVal:newArr, currentPage:1});
}
you need to do something like that in TodoItem --
where --
1- inputArrVal:{} is object And
2- "this.changeInputVal(event, props.item.id)}/>"

Categories