how to pass state to component - reactjs

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} />

Related

Child component not triggering rendering when parent injects props with differnt values

Here are my components:
App component:
import logo from './logo.svg';
import {Component} from 'react';
import './App.css';
import {MonsterCardList} from './components/monster-list/monster-card-list.component'
import {Search} from './components/search/search.component'
class App extends Component
{
constructor()
{
super();
this.state = {searchText:""}
}
render()
{
console.log("repainting App component");
return (
<div className="App">
<main>
<h1 className="app-title">Monster List</h1>
<Search callback={this._searchChanged}></Search>
<MonsterCardList filter={this.state.searchText}></MonsterCardList>
</main>
</div>
);
}
_searchChanged(newText)
{
console.log("Setting state. new text: "+newText);
this.setState({searchText:newText}, () => console.log(this.state));
}
}
export default App;
Card List component:
export class MonsterCardList extends Component
{
constructor(props)
{
super(props);
this.state = {data:[]};
}
componentDidMount()
{
console.log("Component mounted");
this._loadData();
}
_loadData(monsterCardCount)
{
fetch("https://jsonplaceholder.typicode.com/users", {
method: 'GET',
}).then( response =>{
if(response.ok)
{
console.log(response.status);
response.json().then(data => {
let convertedData = data.map( ( el, index) => {
return {url:`https://robohash.org/${index}.png?size=100x100`, name:el.name, email:el.email}
});
console.log(convertedData);
this.setState({data:convertedData});
});
}
else
console.log("Error: "+response.status+" -> "+response.statusText);
/*let data = response.json().value;
*/
}).catch(e => {
console.log("Error: "+e);
});
}
render()
{
console.log("filter:" + this.props.filter);
return (
<div className="monster-card-list">
{this.state.data.map((element,index) => {
if(!this.props.filter || element.email.includes(this.props.filter))
return <MonsterCard cardData={element} key={index}></MonsterCard>;
})}
</div>
);
}
}
Card component:
import {Component} from "react"
import './monster-card.component.css'
export class MonsterCard extends Component
{
constructor(props)
{
super(props);
}
render()
{
return (
<div className="monster-card">
<img className="monster-card-img" src={this.props.cardData.url}></img>
<h3 className="monster-card-name">{this.props.cardData.name}</h3>
<h3 className="monster-card-email">{this.props.cardData.email}</h3>
</div>
);
}
}
Search component:
import {Component} from "react"
export class Search extends Component
{
_searchChangedCallback = null;
constructor(props)
{
super();
this._searchChangedCallback = props.callback;
}
render()
{
return (
<input type="search" onChange={e=>this._searchChangedCallback(e.target.value)} placeholder="Search monsters"></input>
);
}
}
The problem is that I see how the text typed in the input flows to the App component correctly and the callback is called but, when the state is changed in the _searchChanged, the MonsterCardList seems not to re-render.
I saw you are using state filter in MonsterCardList component: filter:this.props.searchText.But you only pass a prop filter (filter={this.state.searchText}) in this component. So props searchTextis undefined.
I saw you don't need to use state filter. Replace this.state.filter by this.props.filter
_loadData will get called only once when the component is mounted for the first time in below code,
componentDidMount()
{
console.log("Component mounted");
this._loadData();
}
when you set state inside the constructor means it also sets this.state.filter for once. And state does not change when searchText props change and due to that no rerendering.
constructor(props)
{
super(props);
this.state = {data:[], filter:this.props.searchText};
}
If you need to rerender when props changes, use componentDidUpdate lifecycle hook
componentDidUpdate(prevProps)
{
if (this.props.searchText !== prevProps.searchText)
{
this._loadData();
}
}
Well, in the end I found what was happening. It wasn't a react related problem but a javascript one and it was related to this not been bound to App class inside the _searchChanged function.
I we bind it like this in the constructor:
this._searchChanged = this._searchChanged.bind(this);
or we just use and arrow function:
_searchChanged = (newText) =>
{
console.log("Setting state. new text: "+newText);
this.setState({filter:newText}, () => console.log(this.state));
}
Everything works as expected.

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>
);
}
}

Converting functional component to class component

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.

trying to pass my arrays (props) into my publish function as selector

import { Mongo } from 'meteor/mongo';
import { Meteor } from 'meteor/meteor';
import React, {Component} from 'react';
import {check} from 'meteor/check';
export const Adressen = new Mongo.Collection('Phonebook');
if (Meteor.isServer) {
Meteor.publish('ArrayToExport', function(branches) {
check(branches, [Match.Any]);
if(branches.length > 10){
return this.ready()
};
return Adressen.find(
{branche: {$in: branches}}, {fields: {firmenname:1, plz:1}}
);
});
}
.
import React, { Component } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import {Adressen} from "../api/MongoDB";
class ExportArray extends Component{
constructor(props){
super(props);
this.state = {
branches: this.props.filteredBranches
};
}
render(){
return(
<div>
<button onClick={this.exportArrays}></button>+
</div>
);
}
}
export default withTracker( (branches) => {
Meteor.subscribe('ArrayToExport', branches);
return {
ArrayToExport: Adressen.find({}).fetch()
};
})(ExportArray);
this.props.filteredBranche is a pure array,generated through controlled input field. this.props.filteredBranches changes as Input changes, in parent Component.
I thought I was sending my this.props.filteredBranches as an argument through withTracker function. But nothing is passed to the publish function.
if (Meteor.isServer) {
arrayExfct = function (array){
return {
find: {branche:{$in: array }},
fields: {firmenname:1, plz:1}
};
}
Meteor.publish('ArrayToExport', function (array) {
return Adressen.find(
arrayExfct(array).find, arrayExfct(array).fields);
});
}
.
export default withTracker( () => {
arrayExfct = function(array) {
return {
find: {branche: {$in: array}},
fields: {firmenname:1, plz:1}
}
}
var array = ['10555'];
Meteor.subscribe('ArrayToExport', array );
var arrayExfct = Adressen.find(arrayExfct(array).find, arrayExfct(array).fields);
return {
ArrayToExport: Adressen.find({}).fetch()
};
})(ExportArray);
It would help if you also added an example of where you used this component and how you pass props to it, but I think I see your problem.
You expect the local state in your rendering component to get into the withTracker container, but that would be the other way around. When you make the withTracker container, you are really making another react component that renders your display component (ExportArray) and passes the data (ArrayToExport) down into it.
So, props go like this currently:
external render -> withTracker component -> ExportArray
What you need to do it to get the filteredBranches (which you pass from a parent component?) from the props argument in withTracker and pass that to the subscribtion,
class ExportArray extends Component{
exportArrays () {
const { ArrayToExport } = this.props;
}
render(){
const { ArrayToExport } = this.props;
return(
<div>
<button onClick={this.exportArrays}></button>+
</div>
);
}
}
export default withTracker(propsFromParent => {
const { filteredBranches } = propsFromParent;
Meteor.subscribe('ArrayToExport', filteredBranches);
return {
ArrayToExport: Adressen.find({}).fetch()
};
})(ExportArray);
Hi the issue is with the code below. The parameter called branches is the props so branches.branches is the array you passed in.
export default withTracker( (branches) => {
Meteor.subscribe('ArrayToExport', branches);
return {
ArrayToExport: Adressen.find({}).fetch()
};
})(ExportArray);
Try the following.
export default withTracker( ({branches}) => {
Meteor.subscribe('ArrayToExport', branches);
return {
ArrayToExport: Adressen.find({}).fetch()
};
})(ExportArray);
Notice all that changed was
(branches)
became
({branches})
I solved my problem with a combination of Session Variables and State.
//Client
import React, { Component } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import {Adressen} from "../api/MongoDB";
import {Meteor} from 'meteor/meteor';
import { Session } from 'meteor/session';
class ExportArray extends Component{
constructor(){
super();
this.state = {
x: [],
y: []
};
this.exportArrays = this.exportArrays.bind(this);
}
exportArrays(e){
e.preventDefault();
this.setState({x: this.props.filteredBranches});
this.setState({y: this.props.filteredPostleitzahlen});
}
render(){
var selector = {branche: {$in: this.state.x},plz: {$in: this.state.y}};
Session.set('selector', selector);
return(
<div>
<button onClick={this.exportArrays}> Commit </button>
</div>
);
}
}
export default withTracker( () => {
const ArrayfürExport = Meteor.subscribe('ArrayToExport', Session.get('selector') );
return {
ArrayToExport: Adressen.find({}).fetch()
};
})(ExportArray);
//Server
Meteor.publish('ArrayToExport', function (selector) {
console.log('von mongodb', selector);
return Adressen.find(
selector
, {
fields: {firmenname:1, plz:1}
});
});
}

Resources