How can I refer to another react component? - reactjs

is there a way to get component's state name, from another component?
I have a component Chat:
import ChatMessage from './ChatMessage'
class Chat extends Component {
state = {
name: 'Bob',
messages: [],
}
getStateName = function(){
return this.state.name
}
}
and another component ChatMessage:
import Chat from './Chat'
class ChatMessage extends Component{
render(){
return(
<p> {Chat.getStateName} </p>
)
}
}
I would like to get as a result 'Bob'.
I was thinking of using a function called getStateName, but it doesn't seem to work. Any idea's how I can fix that?

Ideally state is private and fully controlled by the component. Redux is a library that can be used to manage application wide states via an application store. Refer to redux's getting started to set your app up.

You are on right track to use Components, but you need some changes to look into
here is Stackblitz link for your project
your Chat.js component file will be
import React, { Component } from 'react';
class Chat extends Component {
state = {
name: 'Bob',
messages: []
};
render() {
return <p>{this.state.name}</p>;
}
}
export default Chat;
And ChatMessage.js component will be
import React, { Component } from 'react';
import Chat from './Chat';
class ChatMessage extends Component {
render() {
return <Chat />;
}
}
export default ChatMessage;

Here's a solution that uses a plain ES6 class to store user data, and delegates rendering to a React component.
Either create an instance of the class before accessing it:
class User {
state = {
name: "Bob",
messages: [
"Could you get cucumber from the grocery store?",
"Don't forget the tomatoes, too!",
"Scrap the tomatoes. No tomatoes.",
"Honey, let's get take away instead."
]
};
}
const user1 = new User(); // ← initialising the object instance
const App = () => (
<>
<h1> {user1.state.name} </h1>
<ul>
{user1.state.messages.map((message) => (
<li key={message}>{message}</li>
))}
</ul>
</>
);
export default App;
Or make the state static, which allows you to access it without initialisation:
class User {
static state = { // ← the state object is now static
name: "Bob",
messages: [
"Could you get cucumber from the grocery store?",
"Don't forget the tomatoes, too!",
"Scrap the tomatoes. No tomatoes.",
"Honey, let's get take away instead."
]
};
}
// no need for initialisation
// const user1 = new User();
const App = () => (
<>
<h1> {User.state.name} </h1>
<ul>
{User.state.messages.map((message) => (
<li key={message}>{message}</li>
))}
</ul>
</>
);
export default App;

Related

Array from nested serializer is undefined. REDUX, DRF

I am having trouble displaying the data from a nested serializer. I can get and see the data via Redux and see that it is in my props, but when I try to map it to my React component or the local state it says that it is undefined. I have been at this for a while and I am sure it is a minor issue but it is extremely frustrating.
Business Serializer:
from rest_framework import serializers
from .models import Business
from django.apps import apps as django_apps
from django.conf import settings
def get_job_model():
return django_apps.get_model(settings.JOB_MODEL, require_ready=False)
Job = get_job_model()
# Job Serializer
class JobSerializer(serializers.ModelSerializer):
class Meta:
model = Job
fields = "__all__"
class CreateBusinessSerializer(serializers.ModelSerializer):
jobs = None
class Meta:
model = Business
# fields = '__all__'
exclude = ['owner', 'geolocation']
class GetBusinessSerializer(serializers.ModelSerializer):
jobs = JobSerializer(many=True, read_only=True)
class Meta:
model = Business
# fields = '__all__'
exclude = ['owner', 'geolocation']
class AllBusiness(serializers.ModelSerializer):
class Meta:
model = Business
fields = ['business', 'address']
Business Page Component (abbreviated):
import React, {Component} from 'react';
import {businessDetail, getBusiness} from "../../../actions/business";
import {connect} from 'react-redux';
import store from "../../../store";
import {NOT_HOME} from "../../../actions/types";
import {Link} from "react-router-dom";
class BusinessPage extends Component {
constructor(props) {
super(props);
this.state = {
businessSlug: this.props.match.params.businessSlug,
jobs: this.props.jobs **placed this here so see it in react dev tools**
}
}
componentDidMount() {
this.props.businessDetail(this.state.businessSlug);
}
render() {
return (
<div className='pvt' id='business-page'>
{this.state.jobs.map(job => (
<div key={job.id} className="col-md-6 col-lg-12 item">
<div>
<div className="card">
<Link to={`/job-page/${job.uuid}`} />
</div>
</div>
</div>))}
</div>
);
}
}
function mapStateToProps(state) {
return {
business: state.business.business_page,
jobs: state.business.business_page.jobs,
};
}
export default connect(
mapStateToProps, {businessDetail}
)(BusinessPage);
These pictures will show the problem in more detail
React Dev Tools
Redux Dev Tools showing that it definitely exist
Console log when I try to map it to component
Thank you :)
I believe that this is a lifecycle problem. Both React and Redux are asynchronous so the data that you want might not exist at the moment where you are trying to access it.
this.state.jobs is being set in the constructor so it will store the very first value of this.props.jobs that it sees -- before the action in componentDidMount gets dispatched. If the jobs get set in the redux state as a result of this action, those changes will be reflected in the props but not in the state.
The redux store should be the "single source of truth" so you do not want to duplicate the redux state in your component state. You don't need any component state based on this snippet.
It's not necessary to pass jobs as its own prop since you can access that same variable though this.props.business.jobs. But it's not a problem if you want to keep it separate.
If we think that initial value of the business is undefined or does not have a property jobs, then we need to make sure that we are handling that properly. One simple solution is to replace undefined with an empty array [] so that you can call jobs.map() without errors.
Some of the many ways to safely access a jobs array:
const { jobs = [] } = this.props.business || {};
const jobs = this.props?.business?.jobs || [];
Component code:
class BusinessPage extends Component {
componentDidMount() {
// we don't need state because the slug is in the props
const slug = this.props.match.params.businessSlug;
this.props.businessDetail(slug);
}
render() {
const jobs = this.props?.business?.jobs || [];
return (
<div className="pvt" id="business-page">
{jobs.map((job) => (
<div key={job.id} className="col-md-6 col-lg-12 item">
<div>
<div className="card">
<Link to={`/job-page/${job.uuid}`} />
</div>
</div>
</div>
))}
</div>
);
}
}
function mapStateToProps(state) {
return {
business: state.business.business_page,
};
}
export default connect(
mapStateToProps, {businessDetail}
)(BusinessPage);

Why the data not displayed in nextjs?

I am making a very very simple nextjs application where I am trying to fetch the data from api.
My requirement is I should display the data in layout.js file and this layout.js file is a children in index.js file.
index.js:
import Layout from "./layout";
import React from "react";
class Home extends React.Component {
render() {
return (
<div>
<Layout />
<h4> Main content will be displayed here !! </h4>
</div>
);
}
}
export default Home;
layout.js:
import React from "react";
import fetch from "isomorphic-unfetch";
function Layout(props) {
return (
<div>
<p>Preact has {props.stars} ⭐</p>
<p> Why I couldn't get the above "props.star" ? </p>
</div>
);
}
Layout.getInitialProps = async () => {
console.log("comes into layout getinitial props");
const res = await fetch("https://api.github.com/repos/developit/preact");
const json = await res.json(); // better use it inside try .. catch
return { stars: json.stargazers_count };
};
export default Layout;
So as per the above given code, I have called the layout page inside index.js page (in my real application I need to call like this only so no changes in calling layout inside index)..
But when I made a console.log() in the function Layout.getInitialProps in layout, it doesn't print anything and hence the api data not fetched..
Complete working demo here with code
Why can't I fetch the data inside the layout.js while calling as a children from index.js?
Also provide me the right updated solution to achieve this.. I really searched for many questions but none solved my issue and I couldn't understand those solutions clearly so please help me with the above given example.
That because getInitialProps can only be added to the default component exported by a page, adding it to any other component won't work.
You should use componentDidMount() or useEffect instead, or move getInitialProps in the index and then pass the result to the component. something like (not tested) :
index.js :
import Layout from "./layout";
import React from "react";
class Home extends React.Component {
render() {
return (
<div>
<Layout />
<h4> Main content will be displayed here !! </h4>
</div>
);
}
}
export default Home;
layout.js
import React from "react";
import fetch from "isomorphic-unfetch";
class Layout extends React.Component {
constructor(props) {
super(props);
this.state = {
stars: false
};
}
async componentDidMount() {
console.log("comes into layout getinitial props");
const res = await fetch("https://api.github.com/repos/developit/preact");
const json = await res.json(); // better use it inside try .. catch
this.setState({ stars: json.stargazers_count });
}
render() {
const { stars } = this.state;
return (
<div>
<p>Preact has {stars} ⭐</p>
<p> Why I couldn't get the above "props.star" ? </p>
</div>
);
}
}
export default Layout;
Edit:
Example with class component
Bonus: If you want to add the layout for all the pages of your app this isn't the best approach, instead you should take a look to custom _app.js, example

Passing data into another component

I'm trying to pass data into another component, but I'm having trouble re-rendering state so it's not a blank array when it passes the component.
Movie Card Component:
import React, { Component } from 'react';
import getMovies from './MovieAPI.js';
import MoviePoster from './MoviePoster.js';
class MovieCardII extends Component {
constructor() {
super();
this.state = {
movies: [],
};
}
componentDidMount() {
getMovies().then(results => {
this.setState(results.Search: movies)
console.log("state", this.state);
console.log(results.Search);
});
}
render() {
const { movies } = this.state;
return (
<div>
<h1> Hi </h1>
<MoviePoster movies={movies} />
</div>
);
}
}
export default MovieCardII;
MoviePoster Component
import React from 'react';
import PropTypes from 'prop-types';
const MoviePoster = props => {
const { movies } = props;
console.log(movies);
return (
<div>
{movies.map(movie => (
<div>
<h1> {movie.Poster} </h1>
</div>
))}
</div>
);
};
MoviePoster.propTypes = {
movies: PropTypes.array.isRequired,
};
export default MoviePoster;
I'm using the OMDB API and the MovieCard component is able to get a state with an array list of 10 once I am able to get the get request in.
But in the MoviePoster component, the movie array remains an empty array.
New to React so I'm struggling to understand how to pass data into another component. I am able to get the create the view I want if I don't have to pass data to another array, but I need to create one since the API i'm using is not able to get all the information I need to make another API request using the movie ID in another component later. So basically the set up will be
movieCard is the parent
movie poster, and movieInfo will be the children.
The movieInfo will pull another API request using the imdbID that I get from the movieCard component.
The way you have set the state is wrong
Movie Card Component:
componentDidMount(){
getMovies().then(results=> {
this.setState(results.Search: movies)
console.log("state", this.state);
console.log(results.Search);
});
}
Solution: Movie Card Component
componentDidMount(){
getMovies().then(results=> {
this.setState({movies: results.Search})
console.log("state", this.state);
console.log(results.Search);
});
}
One more change is required to MoviePoster Component
You need to specify key whenever you are looping
const MoviePoster = (props) => {
const { movies } = props;
console.log(movies);
return (
<div>
{movies.map(movie => (
<div key={movies.id}>
<h1> {movie.Poster}</h1>
</div>
))}
</div>
)
}
Hope this solution will help you.

React - passing object through props

I am new to React and trying to pass object through attributes but getting following error.
Uncaught Invariant Violation: Objects are not valid as a React child (found: object with keys {title}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of MeetingComponent.
Here is my code:
Main.jsx
import React from 'react';
import MeetingComponent from '../components/Meeting.jsx';
let meeting = {
title: 'Some title'
};
class AppComponent extends React.Component {
render() {
return (
<div className="index">
<MeetingComponent dataMeeting={meeting} />
</div>
);
}
}
AppComponent.defaultProps = {};
export default AppComponent;
Meeting.jsx
import React from 'react';
class MeetingComponent extends React.Component {
render() {
return (
<div>{this.props.dataMeeting}</div>
);
}
}
MeetingComponent.defaultProps = {};
export default MeetingComponent;
How can I solve this? What is the best practice?
The problem is here
<div>{this.props.dataMeeting}</div>
You cannot render an object in React, maybe you were trying to do
<div>{this.props.dataMeeting.title}</div>
If you want to pass properties of an object , it can be done as follows:
<MeetingComponent {...meeting} />
To access title of the meeting object inside the Meeting component, you can do it by calling it directly this.props.title
You can render it as <div>{this.props.title}</div>
i've used this method to pass object through component
let meeting = {
title: 'Some title'
};
class AppComponent extends React.Component {
render() {
const jsonData =JSON.stringify(meeting);
return (
<div className="index">
<MeetingComponent dataMeeting={jsonData } />
</div>
);
}
}
class MeetingComponent extends React.Component {
render() {
const data = JSON.parse(this.props.dataMeeting);
return (
<div>{data}</div>
<div>{data.title}</div>
);
}
}
static propTypes = {
pieChartSortSettings: PropTypes.shape({
type: PropTypes.string.isRequired,
order: PropTypes.string.isRequired,
}),
};
Here is a example how to pass object in props. You can also checkout methods like: PropTypes.arrayOfand PropTypes.objectOf(more: https://reactjs.org/docs/typechecking-with-proptypes.html)
Best practice is make your props as plain as possible, in your case it may be
<MeetingComponent title={ meeting.title } />
class MeetingComponent extends React.Component {
propTypes = {
title: React.PropTypes.string.isRequired
}
render() {
return (
<div>title: { this.props.title }</div>
);
}
}
And always use propTypes for better warning messages

Redux Simple Router pushPath not updating URL

I ported redux-simple-router into a boilerplate react/redux isomorphic kit (https://github.com/erikras/react-redux-universal-hot-example). I have a simple click event handler that calls 'pushPath' from redux-simple-router. However, pushPath doesn't seem to update my URL. I already implemented the initial port (syncReduxAndRouter) and other routes seem to work fine (other routes use updatePath). Is there something else I need to do to get this to work?
import React, {Component} from 'react';
import { pushPath } from 'redux-simple-router';
import {connect} from 'react-redux';
#connect(null,
{ pushPath })
export default class MyContainer extends Component {
constructor() {
super();
this.state = { links: [{key: 0, name: 'Link1'}, {key: 1, name: 'Link2'}, {key: 2, name: 'Link3'}] };
}
// pass in redux actions as props
handleClick(value) {
pushPath('/Links/' + value);
}
render() {
return (
<div className="container">
<div>Search bar here</div>
<div className={styles.tile_container}>
Tiles here
{this.state.links.map(source =>
<div name={link.name} key={link.key} className={styles.source_tile} onClick= {this.handleClick.bind(this, link.name)}>{link.name}</div>
)}
</div>
</div>
);
}
}
Here's the version of my code with the fix. I needed to use an instance of redux-simple-router that was connected to the store and then pass its methods to the component as a prop.
import React, {Component, PropTypes} from 'react';
import { pushPath } from 'redux-simple-router';
import {connect} from 'react-redux';
#connect(null,
{ pushPath })
export default class MyComponent extends Component {
static propTypes = {
pushPath: PropTypes.func.isRequired
};
constructor() {
super();
this.state = { links: [{key: 0, name: 'Link1'}, {key: 1, name: 'Link2'}, {key: 2, name: 'Link3'}] };
}
// pass in redux actions as props
handleClick(value) {
this.props.pushPath('/Links/' + value);
}
render() {
return (
<div className="container">
<div>Search bar here</div>
<div className={styles.tile_container}>
Tiles here
{this.state.links.map(source =>
<div name={link.name} key={link.key} className={styles.source_tile} onClick= {this.handleClick.bind(this, link.name)}>{link.name}</div>
)}
</div>
</div>
);
}
}
You are calling action creator pushPath instead of the bound method.
Action creator just returns plain object. To call bound method, you should do
handleClick(value) {
this.props.pushValue('/Links/' + value);
}
#connect create proper methods for dispatching and propagate it to you via props.

Resources