Converting functional component to class component - reactjs

I have one functional component, but as I need to use now state and more complex logic, I would like to convert it to class component.
But I don't know exactly how to get it working:
My functional component:
import React from 'react';
const FileList = (props) => {
const items = props.items.map((item) => {
return <p key={item.reqId} > { item.name }</ p>
});
return <div>{items}</div>
}
And I tried to do that:
export default class FileL extends React.Component {
constructor(props) {
super(props)
}
render() {
const { items } = this.props;
items = props.items.map((item) => {
return <p key={item.reqId} > {item.name}</ p>
});
return (
<div>{items}</div>
);
}
}
But this is not working.It says "items" is read-only.
I would like to keep the same functionality.
Any ideas?

In your render function
render() {
const { items } = this.props;
items = props.items.map((item) => {
return <p key={item.reqId} > {item.name}</ p>
});
return (
<div>{items}</div>
);
}
items is const so you can't override it. This has nothing to do with React. And you shouldn't reassign a props element, even if its defined with let. You might use the following:
render() {
const { items } = this.props;
return (
<div>
{
items.map((item) => <p key={item.reqId} > {item.name}</ p>)
}
</div>
);
}

You can try this,
export default class FileL extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{
this.props.items.map((item) => {
return <p key={item.reqId} > {item.name}</ p>
})
}
</div>
);
}
}
Actually you don't need to convert your component to class based component, as React 16.8 comes with Hooks. Using Hooks you can do whatever you can do with class based component. They let you use state and other React features without writing a class.

Related

Pass original component's function to react HOC

I have created react HOC component as below.
const UpdatedComponent = (OriginalComponent) => {
class NewComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
counter:0
}
}
componentDidMount(){
}
incrementCount = () => {
this.setState(prevState => {
return {counter:prevState.counter+1}
})
}
render(){
return <OriginalComponent
incrementCount={this.incrementCount}
count={this.state.counter}
/>
}
}
return NewComponent
}
export default UpdatedComponent
and I am using that component in the below example
class HoverCounter extends Component {
componentDidMount(){
}
handleMessages = () => {
// need to do somthing
}
render() {
const {incrementCount, count} = this.props
return (
<div onMouseOver={incrementCount}>
Hoverd {count} times
</div>
)
}
}
export default UpdatedComponent(HoverCounter)
I want to know that is it possible to pass
handleMessages()
function to HOC?
like this
export default UpdatedComponent(HoverCounter,handleMessages)
I have no idea how to pass the original component function or props to HOC.
you could get everyThing in your Hoc like this :
const UpdatedComponent = (OriginalComponent , func) => {
componentDidMount(){
func()
}
in HoverCounter also you could add this changes:
static handleMessages(){
// need to do something
}
export default UpdatedComponent(HoverCounter , HoverCounter.handleMessages)

Reuse same DOM element in two different react components

I have a small question.
Let's imagine I have component A which holds , after component A does it's job I render component B. I would like that same DOM element (textarea) would be reused in component B.
The reason is if new textarea is rendered in component B it loses focus as it's just new DOM element. It's like after component A lifetame take textarea element from it and just put it in component B instead of rendering new one.
Sample APP
https://jsfiddle.net/remdex/v67gqyLa/1/#&togetherjs=aiRvTGhRK2
class AComponent extends React.Component {
render() {
return ( <textarea>A Component</textarea> )
}
}
class BComponent extends React.Component {
render() {
return ( <textarea>Should be A Component text</textarea> )
}
}
class ABComponent extends React.Component {
constructor(props) {
super(props)
this.state = {'component' : 'A'};
}
render() {
return (
<div><button onClick={(e) => this.setState({component:'B'})}>Switch to B Component</button>
{this.state.component == 'A' && <AComponent/>}
{this.state.component == 'B' && <BComponent/>}
</div>
)
}
}
ReactDOM.render(<ABComponent />, document.querySelector("#app"))
In your sandbox example, ComponentA and ComponentB are redundant. You can create ComponentA and ComponentB as a class if they are using same element and operate them with ComponentAB. You can change your ComponentAB like:
class A {
handle(input) {
// Do your A job here and return result
console.log("Handler A is running");
};
}
class B {
handle(input) {
// Do your B job here and return result
console.log("Handler B is running");
};
}
class ABComponent extends React.Component {
currentHandler = new A();
handleClick = () => {
this.currentHandler = new B();
};
handleChange = (event) => {
// Handle the input with current handler
var result = this.currentHandler.handle(event.target.value);
// If you want you can use result to cahnge something in view
// this.setState({value: result});
}
render() {
return (
<div>
<button onClick={this.handleClick}>
Switch to B Component
</button>
<textarea onChange={this.handleChange}>Text Area used between A class and B class</textarea>
</div>
)
}
}
I also edit the codebox example. You can find it here.
This can be achieved using a ref. ABComponent passes a ref to BComponent to attach to the textarea. When the state of ABComponent updates to component = 'B', then the ref is used to set focus. Use a ref passed to AComponent to grab its textarea value before it's unmounted, then set the value of the textarea in B to it.
import React, { Component, createRef } from "react";
...
class AComponent extends Component {
render() {
const { textareaRef } = this.props;
return <textarea ref={textareaRef} defaultValue="A Component" />;
}
}
class BComponent extends Component {
render() {
const { textareaRef } = this.props;
return <textarea ref={textareaRef} defaultValue="Should be A Component text" />;
}
}
class ABComponent extends Component {
state = { component: "A" };
refA = createRef();
refB = createRef();
componentDidUpdate(prevProps, prevState) {
const { component, content } = this.state;
if (prevState.component !== component) {
if (component === "B") {
this.refB.current.focus();
this.refB.current.value = content;
}
}
}
render() {
return (
<div>
<button
onClick={e =>
this.setState({ component: "B", content: this.refA.current.value })
}
>
Switch to B Component
</button>
{this.state.component === "A" && <AComponent textareaRef={this.refA} />}
{this.state.component === "B" && <BComponent textareaRef={this.refB} />}
</div>
);
}
}

React- Destruct State error in Typescript

I am trying to implement a react component for AutoComplete by referring to a Tutorial.I am using Typescript for my development. When I try to destruct the state inside the render suggestions method the TSLint compiler shows an error saying 'Property Suggestions does not exist on type {}'. Could any one help me to overcome this. It is a big big help.
import * as React from 'react';
export default class Autocomplete extends React.Component<{},{}> {
items: string[];
constructor( props){
super(props);
this.state = {
suggestions :[],
};
this.items =['David','Damian','sara'];
}
render() {
return (
<div>
<input
type="text" onChange={this.onTextChanged}
/>
{this.renderSuggestions()}
</div>
);
}
onTextChanged = (e) =>{
console.log(e.target.value);
const value = e.target.value;
let suggestions =[];
if(value.legth>0){
const regex = new RegExp(`^${value}`,'i');
suggestions = this.items.sort().filter(v => regex.test(v));
}
this.setState(()=> ({suggestions}));
}
renderSuggestions() {
const {suggestions } = this.state;
if(suggestions.length === 0){
return null;
}
return (
<ul>
{suggestions.map((item)=> <li>{item}</li>)}
</ul>
);
}
}
[EDIT : Attached the Screen Capture]
The error was in your
if(value.legth>0){}, you missed n for length. It was just a typo error.
Also I tried the typescript version and this was the only mistake
Link to typescript version
Updated working code is
import * as React from 'react';
export default class Autocomplete extends React.Component {
items=[];
constructor( props){
super(props);
this.state = {
suggestions :[],
};
this.items =['David','Damian','sara'];
}
render() {
return (
<div>
<input
type="text" onChange={this.onTextChanged}
/>
{this.renderSuggestions()}
</div>
);
}
onTextChanged = (e) =>{
console.log(e.target.value);
const value = e.target.value;
let suggestions =[];
if(value.length>0){
const regex = new RegExp(`^${value}`,'i');
suggestions = this.items.sort().filter(v => regex.test(v));
}
this.setState(()=> ({suggestions}));
}
renderSuggestions() {
const {suggestions } = this.state;
if(suggestions.length === 0){
return null;
}
return (
<ul>
{suggestions.map((item)=> <li>{item}</li>)}
</ul>
);
}
}
You specify in your component that the state is of type {} by doing React.Component<{}>. You can usually avoid this by letting typescript infer the state but it has a bit of a problem doing that when the state is assigned inside the constructor. You can usually fix these by assigning them directly as fields inside the class like
export default class Autocomplete extends React.Component {
items = ['David','Damian','sara']
state = { suggestions :[] }
// rest of your component here
}
You can do this in your current class because you don't use the constructor for anything other than setting state, but if that's not an option, you can make sure typescript understands the state properly by assigning it as an explicit generic argument for your component like such.
interface AutocompleteState {
suggestions: Suggestion[]
}
export default class Autocomplete extends React.Component<AutocompleteState> {
constructor() {
// ...
}
}
By Considering all the comments and bit of struggling and some readings I made it work. Thank you for all your suggestions.
import * as React from 'react';
import { Suggestions } from 'office-ui-fabric-react';
export interface IServiceState {
suggestions ?: string []
}
export default class Autocomplete extends React.Component<{},IServiceState> {
items =['David','Damian','sara'];
constructor( props){
super(props);
this.state = {
suggestions : [],
};
}
render() {
return (
<div>
<input
type="text" onChange={this.onTextChanged}
/>
{this.renderSuggestions()}
</div>
);
}
onTextChanged = (e) =>{
console.log(e.target.value);
const value = e.target.value;
let suggestions :string[];
if(value.length>0){
const regex = new RegExp(`^${value}`,'i');
suggestions = this.items.sort().filter(v => regex.test(v));
console.log(suggestions);
}
//this.setState(()=> ({suggestions1}));
//this.setState()
this.setState({suggestions : suggestions});
}
renderSuggestions() {
const suggestions :string[]= this.state.suggestions;
console.log('render Suggestions');
console.log(this.state.suggestions);
console.log('render1 Suggestions');
if(Suggestions.length === 0){
return null;
}
return (
<ul>
{suggestions.map((item)=> <li>{item}</li>)}
</ul>
);
}
}

how to pass state to component

I created MExample component in that component i have created this
export default class MExample extends Component {
_validate() {
if (validateDate(this.state.choseDate).status) {
if (validateList(this.state.list).status) {
var list = this.state.list;
var choseDate = this.state.choseDate;
console.log(list+choseDate)
this.setState({ visibleModal: null , list:[], choseDate:''})
} else {
alert("select list date")
}
} else {
alert("select monthly date ")
}
}
render() {
return (
// jsx
)}
export default class Mnavigate extends Component {
render() {
return (
<MExample list={this.state.list} choseDate = {this.state.choseDate}/>
// can i access value like this ?
)
}
How to use this.state.list and this.state.choseDate in other component in which i'm importing this component <MExample here i want list and choseDate value />
<MExample list={this.state.list} choseDate={this.state.choseDate} />
and inside MExample component
access through
this.props.list and this.props.choseDate
class MExample extends React.Component{
render(){
console.log(this.props.list);
return null;
}
}
You can create properties and pass them as props.
Create a component as below
import React, { Component } from 'react'
class MExample extends Component {
// You can access them via this.props
validate = () => {
console.log(this.prop.list);
console.log(this.prop.choseDate);
}
render() {
let {list,choseDate} = this.props;
// your code comes here
return (
<div>
</div>
)
}
}
export default MExample;
Pass the state in the properties.
<MExample list={this.state.list} choseDate={this.state.choseDate} />

Pass state value to component

I am really new in React.js. I wanna pass a state (that i set from api data before) to a component so value of selectable list can dynamically fill from my api data. Here is my code for fetching data :
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
From that code, i set a state called item. And i want to pass this state to a component. Here is my code :
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
But i get an error that say
TypeError: Cannot read property 'item' of undefined
I am sorry for my bad explanation. But if you get my point, i am really looking forward for your solution.
Here is my full code for additional info :
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {List, ListItem, makeSelectable} from 'material-ui/List';
import Subheader from 'material-ui/Subheader';
let SelectableList = makeSelectable(List);
function wrapState(ComposedComponent) {
return class SelectableList extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
};
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
componentWillMount() {
this.setState({
selectedIndex: this.props.defaultValue,
});
this.getListSiswa();
}
handleRequestChange = (event, index) => {
this.setState({
selectedIndex: index,
});
};
render() {
console.log(this.state.item);
return (
<ComposedComponent
value={this.state.selectedIndex}
onChange={this.handleRequestChange}
>
{this.props.children}
</ComposedComponent>
);
}
};
}
SelectableList = wrapState(SelectableList);
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
export default ListSiswa;
One way to do it is by having the state defined in the parent component instead and pass it down to the child via props:
let SelectableList = makeSelectable(List);
function wrapState(ComposedComponent) {
return class SelectableList extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
};
componentWillMount() {
this.setState({
selectedIndex: this.props.defaultValue,
});
this.props.fetchItem();
}
handleRequestChange = (event, index) => {
this.setState({
selectedIndex: index,
});
};
render() {
console.log(this.state.item);
return (
<ComposedComponent
value={this.state.selectedIndex}
onChange={this.handleRequestChange}
>
{this.props.children}
{this.props.item}
</ComposedComponent>
);
}
};
}
SelectableList = wrapState(SelectableList);
class ListSiswa extends Component {
state = {
item: {}
}
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
render() {
return (
<SelectableList item={this.state.item} fetchItem={this.getListSiswa}>
<Subheader>Daftar Siswa</Subheader>
</SelectableList>
);
}
}
export default ListSiswa;
Notice that in wrapState now I'm accessing the state using this.props.item and this.props.fetchItem. This practice is also known as prop drilling in React and it will be an issue once your app scales and multiple nested components. For scaling up you might want to consider using Redux or the Context API. Hope that helps!
The error is in this component.
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
This component is referred as Stateless Functional Components (Read)
It is simply a pure function which receives some data and returns the jsx.
you do not have the access this here.

Resources