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);
Related
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;
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);
}
}
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
I have the structure of components (nested) that seems like this:
Container
ComponentA
ComponentB
ComponentC(want to handle event here with state that lives on container)
Do I need to pass as props all the way from Container, ComponentA, ComponentB and finally ComponentC to have this handler? Or is there another way like using Context API?
I'm finding a bit hard to handle events with react.js vs vue.js/angular.js because of this.
I would recommend using either Context API (as you mentioned) or Higher Order Components (HoC)
Context Api is your data center. You put all the data and click events that your application needs here and then with "Consumer" method you fetch them in any component regardless of how nested it is. Here is a basic example:
context.js //in your src folder.
import React, { Component, createContext } from "react";
import { storeProducts } from "./data"; //imported the data from data.js
const ProductContext = createContext(); //created context object
class ProductProvider extends Component {
state = {
products: storeProducts,
};
render() {
return (
<ProductContext.Provider
//we pass the data via value prop. anything here is accessible
value={{
...this.state,
addToCart: this.addToCart //I wont use this in the example because it would
be very long code, I wanna show you that, we pass data and event handlers here!
}}
>
// allows all the components access the data provided here
{this.props.children},
</ProductContext.Provider>
);
}
}
const ProductConsumer = ProductContext.Consumer;
export { ProductProvider, ProductConsumer };
Now we set up our data center with .Consumer and .Provider methods so we can access
here via "ProductConsumer" in our components. Let's say you want to display all your products in your home page.
ProductList.js
import React, { Component } from "react";
import Product from "./Product";
import { ProductConsumer } from "../context";
class ProductList extends Component {
render() {
return (
<React.Fragment>
<div className="container">
<div className="row">
<ProductConsumer>
//we fetch data here, pass the value as an argument of the function
{value => {
return value.products.map(product => {
return <Product key={product.id} />;
});
}}
</ProductConsumer>
</div>
</div>
</React.Fragment>
);
}
}
export default ProductList;
This is the logic behind the Context Api. It sounds scary but if you know the logic it is very simple. Instead of creating your data and events handlers inside of each component and prop drilling which is a big headache, just put data and your event handlers here and orchestrate them.
I hope it helps.
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