How do i put entities into props after fetching it from componetDidMount? - javascript

inside componentDidMount Im calling the dispatched fetchReviews. but once mounted how does the fetched reviews get set in props?
business show component:
import React from "react";
import { Link } from 'react-router-dom';
import Star from "../star/star";
class BusinessShowIndex extends React.Component {
constructor(props) {
super(props)
this.state = {
loading: true
}
}
componentDidMount() {
this.props.fetchReviews(this.props.business.id)
// .then (() => this.setState({reviews: this.props.reviews}))
this.setState({loading: false})
console.log(this.props)
}
render() {
const { business, reviews } = this.props;
if (!this.props.reviews) return null;
if (this.state.loading === true) {
return <p>Loading...</p>
}
return (
<div className="business-show">
<Link to={`/business/${business.id}`} className='link-business-index'>
<img className="business-index-photo" src={business.photo_urls[0]} alt=""/>
<p className="business-index-name">{business.name}</p>
<Star reviews={reviews}/>
<p className="business-index-city">{business.city}</p>
<p className="business-index-cost">Cost: {business.cost}</p>
<p className="business-index-hours">Hours: {business.open} - {business.close}</p>
</Link>
</div>
)
}
};
export default BusinessShowIndex;
container:
import { connect } from 'react-redux';
import {fetchReviews} from '../../actions/review_actions';
import { withRouter } from 'react-router-dom';
import BusinessShowIndex from './business_show_index';
const mapStateToProps = (state, ownProps) => ({
business: state.entities.businesses[ownProps.business.id],
currentUser: state.entities.users[state.session.id],
reviews: Object.values(state.entities.reviews)
})
const mapDispatchToProps = dispatch => ({
fetchReviews: (businessId) => dispatch(fetchReviews(businessId))
})
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(BusinessShowIndex));
let me know what else you need to see! thank you!
also any advice to clean code? taking any suggestions.
Inside componentDidMount Im calling the dispatched fetchReviews. but once mounted how does the fetched reviews get set in props?

You can get using this.props.review
Check your mapStateToProps function. You get the entire state there and it returns whatsever part of it you want to return.

Related

MapStrateToProps doesn't connect to the local storage using redux and react

I have a web app that is suppose to show a list of notes made by the user on the dashboard if said list exist (that is if the user wrote any note at all). I wrote the reducer, the actions and I connected state and dispatch in order for it to work. But for some reason the notes created don't appear once in the dashboard when I write them, I already made sure that the ADD_NOTE action gets fired and that the reducer updates the data in redux, but in the dashboard component that data disappears.
This is my reducer.
export default (state = [], action) => {
switch (action.type) {
case "ADD_NOTE":
return [
...state,
action.note
];
case "REMOVE_NOTE":
return state.filter(({ id }) => id !== action.id);
default:
return state;
}
}
And those are my actions
import { v4 as uuidv4 } from 'uuid';
export const addNote = ({ title = "", body = ""} = {}) => ({
type: "ADD_NOTE",
note : {
title,
body,
id : uuidv4()
}
});
export const removeNote = ({ id } = {}) => ({
type: "REMOVE_NOTE",
id
});
This is the component that holds the create note form.
import React, { Component } from 'react';
class CreateNote extends React.Component{
constructor(props){
super(props);
this.onTitleChange = this.onTitleChange.bind(this);
this.onBodyChange = this.onBodyChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
title: "",
body: "",
error: ""
}
}
onTitleChange(e){
const title = e.target.value;
this.setState({ title });
}
onBodyChange(e){
const body = e.target.value;
this.setState({ body });
}
onSubmit(e){
e.preventDefault();
if(!this.state.title || !this.state.body){
this.setState({ error : "Please fill in all gaps"});
} else {
this.setState({ error: ""});
const data = { title: this.state.title, body: this.state.body}
this.props.onChange(data);
}
}
render(){
return(
<div>
{this.state.error && <p>{this.state.error}</p>}
<form onSubmit = {this.onSubmit}>
<label>Put a title for your note</label>
<input
placeholder="Title"
type="text"
value={this.state.title}
autoFocus
onChange = {this.onTitleChange}
/>
<label>Write your note</label>
<textarea
placeholder="Note"
value={this.state.body}
autoFocus
onChange = {this.onBodyChange}
/>
<input type="submit" value="Submit"/>
</form>
</div>
);
}
}
export default CreateNote;
And this is the component that fires the ADD_NOTE action
import React, { Component } from 'react';
import CreateNote from "./actions/CreateNote";
import Header from "./Header";
import { addNote } from "../actions/noteActions"
import { connect } from 'react-redux';
class Create extends React.Component{
constructor(props){
super(props);
this.eventHandler = this.eventHandler.bind(this);
}
eventHandler(data){
this.props.addNote(data);
this.props.history.push("/");
}
render(){
return (
<div>
<Header />
<CreateNote onChange = {this.eventHandler}/>
</div>
)
}
}
const mapDispatchToProps = (dispatch) => ({
addNote: (note) => dispatch(addNote(note))
});
export default connect(null, mapDispatchToProps)(Create);
And finally this is the dashboard component that renders the notes if they exist
import React from "react";
import ListItem from "./actions/ListItem";
import { connect } from 'react-redux';
const ListGroup = (props) => (
<div>
{
props.notes.length === 0 ? <h1>Write a note!</h1> :
(
props.notes.map((note) => {
return <ListItem key={note.id} {...note} />;
})
)
}
</div>
)
// The mapStateToProps does not connect with the local state, the action ADD_NOTE fires whenever
// the Create form is submited and the reducer updates the redux storage. So the problem lies here ?
// It could be that state.note is not definded but I don't know where should I define it if I have to,
// and apparently I don't have to ???????????????
const mapStateToProps = (state) => {
return {
notes: state.note
};
};
export default connect(mapStateToProps)(ListGroup);
When I try to run this it fires an error:
ListGroup.js?11a1:5 Uncaught TypeError: Cannot read property 'length' of undefined
at ListGroup (ListGroup.js?11a1:5)
Showing that the data that gets passed to the props is undefined. I'm thinking that it could be that state.note is not defined and I have to define it somewhere but I don't know if that's the case.
Use Hooks in functional components
connect() is only valid for class based components. For functional components you need to use hooks. Specifically the useSelector hook for reading redux state and useReducer to emit actions. You can find more instructions on redux hooks here https://react-redux.js.org/api/hooks#useselector

object can't get method or property 'getCars'

i'm working on react-redux intermidiate..but i don't know what's going wrong
on this project
hera i have creacted the searchbar for getting car details..and the file is created as 'search.js'...you can see here..
search.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getCars } from '../actions';
import { bindActionCreators } from 'redux';
class Search extends Component{
constructor(props){
super(props);
this.state = {
keyword:''
}
}
searchCars = (event) => {
event.preventDefault();
this.props.getCars(this.state.keyword)
}
handleChange = (event) => {
this.setState({
keyword:event.target.value
})
}
componentDidMount(){
console.log(this.state);
}
render(){
return(
<div className="main_search">
<form onSubmit={this.searchCars}>
<input type="text" value={this.state.keyword} onChange = {this.handleChange} />
</form>
</div>
)
}
}
// mapStateToProps
// mapDispatchToProps
function mapDispatchToProps(dispatch){
return bindActionCreators({getCars}, dispatch)
}
export default connect(null,mapDispatchToProps)(Search);
and i think error comes from here about getCars..which is described below as s 'index.js'...you can see here
index.js
const URL_ROOT = 'http://localhost:3004'
export default function getCars(keywords){
const request = fetch(`${URL_ROOT}/carsIndex?q=${keywords}`,
{method:'GET'})
.then(response => response.json())
return{
type:'SEARCH_CARS',
payload:request
}
}
and the error looks like this..
and error showing in bundle.js file
so try to fix it and help me...
Please change your mapDispatchToProps method as
const mapDispatchToProps = (dispatch)=> (
bindActionCreators(getCars, dispatch)
)

Using .map() to render Component UI with information from API

I'm currently having a problem trying to get UI to render with React. I'm using information I've received from ShopifyAPI and trying to render it to my component. I'm not sure what to do. Do I need to update the state with information returned from my API? Here's my code at the moment.
ShopifyCatalog.js
import React, { Component } from 'react';
import { Link } from 'react-router'
import styles from '../styles';
import ShopProducts from './ShopProducts'
import { getAllProducts } from '../utils/shopifyHelpers';
export default class ShopCatalog extends Component {
constructor(...args){
super(...args);
this.state = {
allProducts: []
}
}
render() {
let allProducts
getAllProducts()
.then((products) => {
return allProducts = products
})
.then((allProducts) => {
allProducts.map((product) => {
<div className='col-sm-offset-1 col-sm-2'>
<Link to={'shop/${product.id}'}>
<img src={product.images[0].src} />
<h5>{product.title}</h5>
</Link>
</div>
})
})
return (
<div style={styles.productInfo}>
{allProducts}
</div>
)
}
}
I thought it might have something to do with using promises more extensively, but I'm pretty sure it's because my state isn't updating with the information that I'm grabbing from the API. I appreciate your time, thank you.
EDIT:
I've updated my code now and it looks like this
ShopCatalog.js Updated
import React, { Component } from 'react';
import { Link } from 'react-router'
import styles from '../styles';
import ShopProducts from './ShopProducts'
import { getAllProducts } from '../utils/shopifyHelpers';
export default class ShopCatalog extends Component {
constructor(...args){
super(...args);
this.state = {
allProducts: [],
listAllProducts: []
}
}
componentDidMount() {
getAllProducts()
.then((products) => {
this.setState({
allProducts: products
})
})
}
render() {
return (
<div style={styles.productInfo}>
{this.state.allProducts.map((product) => {
<h1>{product.title}</h1>
})}
</div>
)
}
}
But it's still not rendering anything from the map of my state. Is it because map is called while there is nothing in the state? How do I work around this so map get's called and returns UI? Thank you.
Put your request in the componentDidMount lifecycle method, then update your state. Your render method is returning before your request has completed.
export default class ShopCatalog extends Component {
constructor(...args){
super(...args);
this.state = {
allProducts: []
}
}
componentDidMount() {
const _this = this;
getAllProducts()
.then((products) => {
_this.setState({ allProducts: products });
});
}
render() {
return (
<div style={styles.productInfo}>
{this.state.allProducts.map((product) => {
<div className='col-sm-offset-1 col-sm-2'>
<Link to={'shop/${product.id}'}>
<img src={product.images[0].src} />
<h5>{product.title}</h5>
</Link>
</div>
})}
</div>
)
}
}
I assume something like this, not sure specifics to your case, just giving idea how this should look like.
export default class ShopCatalog extends Component {
state = {
allProducts: []
}
getAllProducts = () => {
fetch(...API).then(response => response.json()).then(products =>
this.setState({allProducts: products}));
}
componentDidMount() {
this.getAllProducts()
}
render() {
const {allProducts} = this.state;
return (
<div>
{allProducts.map((product,key) => <div key={key}>
<span>{product.title}</span>
</div>
)}
</div>
)
}
}

redux todomvc: what happens when new one item?

I am learning redux todomvc, and have some questions about the source codes below. Any comments welcomed. Thanks.
Q1: why store.dispatch() and store.subscribe() not called? It seems that this example is a little different from the data flow introduction here.
Q2: can anyone explain what happens when new one item? How src/index.js, src/containers/App.js, src/components/Header.js, src/components/TodoTextInput.js work togother when new one item?
Q3: where are todos and actions from (src/containers/App.js)?
Q4: this state === store.getState() (in src/components/TodoTextInput.js)?
// src/index.js
import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import App from './containers/App'
import reducer from './reducers'
import 'todomvc-app-css/index.css'
const store = createStore(reducer)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
// src/containers/App.js
import React, { PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import Header from '../components/Header'
import MainSection from '../components/MainSection'
import * as TodoActions from '../actions'
const App = ({todos, actions}) => (//Q3: where are todos and actions from?
<div>
<Header addTodo={actions.addTodo} />
<MainSection todos={todos} actions={actions} />
</div>
)
App.propTypes = {
todos: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
}
const mapStateToProps = state => ({
todos: state.todos
})
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(TodoActions, dispatch)
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
// src/components/Header.js
import React, { PropTypes, Component } from 'react'
import TodoTextInput from './TodoTextInput'
export default class Header extends Component {
static propTypes = {
addTodo: PropTypes.func.isRequired
}
handleSave = text => {
if (text.length !== 0) {
this.props.addTodo(text)
}
}
render() {
return (
<header className="header">
<h1>todos</h1>
<TodoTextInput newTodo// where is it from?
onSave={this.handleSave}
placeholder="What needs to be done?" />
</header>
)
}
}
// src/components/TodoTextInput.js
import React, { Component, PropTypes } from 'react'
import classnames from 'classnames'
export default class TodoTextInput extends Component {
static propTypes = {
onSave: PropTypes.func.isRequired,
text: PropTypes.string,
placeholder: PropTypes.string,
editing: PropTypes.bool,
newTodo: PropTypes.bool
}
state = {//Q4: this state === store.getState()?
text: this.props.text || ''
}
handleSubmit = e => {
const text = e.target.value.trim()
if (e.which === 13) {
this.props.onSave(text)
if (this.props.newTodo) {
this.setState({ text: '' })
}
}
}
handleChange = e => {
this.setState({ text: e.target.value })
}
handleBlur = e => {
if (!this.props.newTodo) {
this.props.onSave(e.target.value)
}
}
render() {
return (
<input className={
classnames({
edit: this.props.editing,
'new-todo': this.props.newTodo
})}
type="text"
placeholder={this.props.placeholder}
autoFocus="true"
value={this.state.text}
onBlur={this.handleBlur}
onChange={this.handleChange}
onKeyDown={this.handleSubmit} />
)
}
}
// src/components/TodoItem.js
import React, { Component, PropTypes } from 'react'
import classnames from 'classnames'
import TodoTextInput from './TodoTextInput'
export default class TodoItem extends Component {
static propTypes = {
todo: PropTypes.object.isRequired,
editTodo: PropTypes.func.isRequired,
deleteTodo: PropTypes.func.isRequired,
completeTodo: PropTypes.func.isRequired
}
state = {
editing: false
}
handleDoubleClick = () => {
this.setState({ editing: true })
}
handleSave = (id, text) => {
if (text.length === 0) {
this.props.deleteTodo(id)
} else {
this.props.editTodo(id, text)
}
this.setState({ editing: false })
}
render() {
const { todo, completeTodo, deleteTodo } = this.props
let element
if (this.state.editing) {
element = (
<TodoTextInput text={todo.text}
editing={this.state.editing}
onSave={(text) => this.handleSave(todo.id, text)} />
)
} else {
element = (
<div className="view">
<input className="toggle"
type="checkbox"
checked={todo.completed}
onChange={() => completeTodo(todo.id)} />
<label onDoubleClick={this.handleDoubleClick}>
{todo.text}
</label>
<button className="destroy"
onClick={() => deleteTodo(todo.id)} />
</div>
)
}
return (
<li className={classnames({
completed: todo.completed,
editing: this.state.editing
})}>
{element}
</li>
)
}
}
Q1 why store.dispatch() and store.subscribe() not called?
Because of 'container'. In redux, a container is a component that subscribes to changes in store. This is done with Redux's mapStateToProps ,mapDispatchToProps and finally the connect function calls inside the container file
The connect function does call store.subscribe internally.
Q2: can anyone explain what happens when new one item?
the App container pass on the actions prop to the App component via mapDispatchToProps
This prop actions contains the action addTodo and that is past down to the Header
The Header component call the addTodo action upon the TextInput is saved
The action addTodo is dispatched
The reducer handles the action and update the state with new item. Store is updated.
Store update triggers the App container to rerender with updated props because the App container has mapStateToProps
done
Q3: where are todos and actions from (src/containers/App.js)?
Again this is because of the Redux's connect function. It will get the returned values from both mapStateToProps and mapDispatchToProps, merge them and pass it to the App component as props. todos comes from mapStateToProps and actions is from mapDispatchToProps
Q4 this state === store.getState()
Dont be confused. The state in the TodoTextInput is React's native component state and has nothing to do with Redux's state. However, if you need a state in your app, it is very common that one needs to decide if it should live in a Redux store or in the component itself.
If the state is only relevant to the component itself and no other component needs to know the status of that state, it indicates that it should live inside the component instead of being in Redux's store.
The state in the TodoTextInput component holds user input temporary before user commits the change. It is well fit to be an internal state of the component itself.

Trouble using actions in react-redux presentational component

I'm new to redux and having trouble wrapping my head around presentational and container components.
Relevant stack:
react v0.14.8
react-native v0.24.1
redux v3.5.2
react-redux v4.4.5
The issue:
I have a login button component, which when rendered checks the login status and calls the onSuccessfulLogin action which updates the state with the user's Facebook credentials.
However, when trying to separate this into separate presentational/container components, I'm unable to call the onSuccessfulLogin action: Error: onSuccessfulLogin is not defined.
What am I doing wrong here? I'd imagine there's something simple that I'm not understanding with the relationship between the two components and the connect() function.
Presentational Component (Login.js)
import React, { PropTypes } from "react-native";
import FBLogin from "react-native-facebook-login";
import UserActions from "../users/UserActions";
class LoginPage extends React.Component {
render() {
const { userData, onSuccessfulLogin } = this.props;
return (
<FBLogin
permissions={["email","user_friends"]}
onLoginFound= { data => {
onSuccessfulLogin(data.credentials);
}}
/>
)
}
};
export default LoginPage;
Container Component (LoginContainer.js)
import { connect } from 'react-redux';
import LoginPage from "../login/LoginPage";
import UserActions from "../users/UserActions";
const mapDispatchToProps = (dispatch) => {
return {
onSuccessfulLogin: (userData) => {
dispatch(UserActions.userLoggedIn(userData))
}
}
}
const mapStateToProps = (state) => {
return {
userData: state.userData
}
}
const LoginContainer = connect(
mapStateToProps,
mapDispatchToProps
)(LoginPage);
export default LoginContainer;
Also, if I wanted to make the updated state.userData accessible to the LoginPage component, how would I do that? Any help is appreciated!
Solved! When using ES6 classes, you're required to call super(props) in a constructor method in order to access the container's properties in the connected presentational component:
class LoginPage extends React.Component {
constructor(props){
super(props);
}
render(){
// ...
}
}
Your container component is supposed to be a component and it must have a render function with the dumb/presentational components you want to render.
import { connect } from 'react-redux';
import LoginPage from "../login/LoginPage";
import UserActions from "../users/UserActions";
class LoginContainer extends React.Component {
constructor(props){
super(props);
}
render() {
return (
<LoginPage userData={this.props.userData}
onSuccessfulLogin={this.props.onSuccessfulLogin}
/>
)
}
};
const mapDispatchToProps = (dispatch) => {
return {
onSuccessfulLogin: (userData) => {
dispatch(UserActions.userLoggedIn(userData))
}
}
}
const mapStateToProps = (state) => {
return {
userData: state.userData
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(LoginPage);

Categories

Resources