prop returns Null when component renders - reactjs

I am trying to add some functionality that enables or disables a button depending on whether the user has at least one "credit". I want to use the logical && to determine whether to enabled or disabled the button. The parent component fetches the current user asynchronously, which should give the component access to the user model and the users credits.
CHILD COMPONENT:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import SurveyList from './surveys/SurveyList';
class Dashboard extends Component {
render() {
console.log(this.props);
return (
<div>
<SurveyList />
<div className="fixed-action-btn">
{this.props.auth.credits &&
<Link to="/surveys/new" className="btn-floating btn-large red">
<i className="material-icons">add</i>
</Link>
}
<button className="btn-floating btn-large disabled red">
<i className="material-icons">add</i>
</button>
</div>
</div>
);
}
};
function mapStateToProps(state) {
return {
auth: state.auth
}
}
export default connect(mapStateToProps)(Dashboard);
PARENT COMPONENT:
import React, { Component } from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import Header from './Header';
import { connect } from 'react-redux';
import * as actions from '../actions';
import Landing from './Landing';
import Dashboard from './Dashboard';
import NewList from './lists/NewList';
class App extends Component {
componentDidMount() {
this.props.fetchUser();
}
render() {
console.log(this.props);
return (
<div className="container">
<BrowserRouter>
<div>
<Header />
<Route exact path='/' component={Landing} />
<Route exact path='/surveys' component={Dashboard} />
<Route path='/surveys/new' component={NewList} />
</div>
</BrowserRouter>
</div>
);
}
};
export default connect(null, actions)(App);
ACTION:
export const fetchUser = () => async dispatch => {
const res = await axios.get('/api/currentUser')
dispatch({ type: FETCH_USER, payload: res.data});
};

Add an additional check this.props.auth && this.props.auth.credits &&...

Related

Extract Data from API and show in another page

This question may sound silly to some people, but I am really confused on how to do it
I have 3 file: App.js, HomePage.js and Profile.js
App.js :
import React from "react"
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import HomePage from "./components/HomePage";
import Profile from "./components/Profile"
function App() {
return (
<Router>
<Switch>
<Route path="/" exact component={HomePage} />
<Route exact path="/profile/:profileId" component= {Profile} />
</Switch>
</Router>
);
}
export default App;
From here, the default page it will go to is HomePage.js
HomePage.js:
import React, { Component } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
class HomePage extends Component {
constructor() {
super();
this.state = {
userData: [],
}
}
componentDidMount() {
axios.get("XXXXXXXX").then((response) => {
const userDataList = response.data.users;
this.setState({
userData: userDataList
})
})
}
render() {
const userGrid = this.state.userData.map((user, index) => {
return (
<div key={index}>
<Link to={`/profile/${user.id}`}>
<img src={user.profilepicture} />
<p>{user.name}</p>
</Link>
</div>
)
})
return (
<div className="App">
<div className="card">
<div className="card__top">
<span className="card__title">
<p>Select An Account</p>
</span>
</div>
<div className="card__bottom">
<div className="card__table">
{userGrid}
</div>
</div>
</div>
</div>
)
}
}
export default HomePage;
In HomePage.js, I am able to show the profile picture and name of the user from API.
In the next page which is Profile.js , I am able to print the ID of the user.
Profile.js:
import React, { Component } from "react";
class Profile extends Component{
componentDidMount(){
const uid = this.props.match.params.profileId;
}
render() {
console.log(this.props.match);
return(
<h1>{this.props.match.params.profileId}</h1>
)
}
}
export default Profile;
As you can see I am printing the ID of user.
Here I also want to show the Profile Picture of the user which I selected in HomePage.js
This I am not able to do it.
JSON file:
{ - users: [-{id:1, name:"abc", profilepicture: "xxxxx.jpeg"}, ]}
You need to store a global state in your applicattion, which you can access from every connected component. This is a more complex topic. redux is a good framework to handle your global state changes.
Here is a tutorial: https://appdividend.com/2018/06/14/how-to-connect-react-and-redux-with-example/
I found it pretty hard to learn redux, but in the end it takes away a lot of pain. Because this is a problem you gonna have in every app you build with react.
You need use Context API o redux
Example context API: https://ibaslogic.com/react-context-api/
Context's well to little projects, but Redux performs better.
App.js
import React from "react"
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import HomePage from "./components/HomePage";
import Profile from "./components/Profile"
import { UsersProvider } from "./UsersProvider.js";
function App() {
return (
<Router>
<UsersProvider>
<Switch>
<Route path="/" exact component={HomePage} />
<Route exact path="/profile/:profileId" component= {Profile} />
</Switch>
</UsersProvider>
</Router>
);
}
export default App;
UsersContext.js
import React, { Component } from "react"
const UsersContext = React.createContext();
const UsersProvider = UsersContext.Provider;
const UsersConsumer = TodosContext.Consumer;
class MyContext extends Component {
state = {
value: null,
};
setValue = (value) => {
this.setState({ value });
};
render() {
return (
<UsersProvider value={{ setValue, value }}>{this.props.children}
</UsersProvider>
)
}
}
export { UsersContext, UsersProvider, UsersConsumer }
HomePage.js
import React, { Component } from "react";
import axios from 'axios';
class HomePage extends Component {
componentDidMount() {
axios.get("XXXXXXXX").then((response) => {
const userDataList = response.data.users;
// updating your context
this.props.context.setValue(userDataList);
})
}
render() {
const userGrid = this.props.context.value.map((user, index) => {
return (
<div key={index}>
<Link to={`/profile/${user.id}`}>
<img src={user.profilepicture} />
<p>{user.name}</p>
</Link>
</div>
)
})
return (
<div className="App">
<div className="card">
<div className="card__top">
<span className="card__title">
<p>Select An Account</p>
</span>
</div>
<div className="card__bottom">
<div className="card__table">
{userGrid}
</div>
</div>
</div>
</div>
)
}
}
export default HomePage;
Profile.js
import React, { Component } from "react";
import { UsersConsumer } from "./UsersContext.js";
class Profile extends Component{
render() {
return(
<UsersConsumer>
{users => (
<h1>{users.value.find(user => user.id === this.props.match.params.profileId)}</h1>
)}
</UsersConsumer>
)
}
}
export default Profile;

How do you access match in a React-Redux container?

Firstly, thank you to anyone who is reading this and is willing to help!
I'm trying to build a react-redux web app, and I'm having trouble accessing an id from the url in a container. The url looks like this: websitename.com/games/:game_id
I need to access that :game_id so that I can use it in a redux action to make a call to my api, but I can't figure out how to access the usage of match. I get the following error when I try to compile:
./src/containers/GameDetails.js
Line 9:19: 'match' is not defined no-undef
My app is set up with the following structure: Main.js houses the routes:
import React from "react";
import {Switch, Route, withRouter, Redirect} from "react-router-dom";
import {connect} from "react-redux";
import Homepage from "../components/Homepage";
import AuthForm from "../components/AuthForm";
import {authUser} from "../store/actions/auth";
import {removeError} from "../store/actions/errors"
import withAuth from "../hocs/withAuth";
import GameForm from "./GameForm";
import GamePage from "../components/GamePage";
const Main = props => {
const {authUser, errors, removeError, currentUser} = props;
return (
<div className="container">
<Switch>
<Route path="/" exact render={props => <Homepage currentUser={currentUser} {...props} /> } />
<Route
path="/signin" exact
render={props => {
return(
<AuthForm
removeError={removeError}
errors={errors}
onAuth={authUser}
buttonText="Log in"
heading="Welcome Back."
{...props}
/>
)
}} />
<Route
path="/signup" exact
render={props => {
return(
<AuthForm
removeError={removeError}
errors={errors}
onAuth={authUser}
signUp
buttonText="Sign me up"
heading="Join Weekly Matchup today."
{...props}
/>
)
}}
/>
<Route
path="/games/new" exact
component={withAuth(GameForm)}
/>
<Route
path="/games/:game_id"
render={props => {
return(
<GamePage
currentUser={currentUser}
{...props}
/>
)
}}
/>
<Redirect to="/" />
</Switch>
</div>
)
}
function mapStateToProps(state){
return {
currentUser: state.currentUser,
errors: state.errors
};
}
export default withRouter(connect(mapStateToProps, {authUser, removeError})(Main));
GamePage.js is a component that has the GameDetails container in it:
import React from "react";
import { Link } from "react-router-dom";
import GameDetails from "../containers/GameDetails";
const GamePage = ({ currentUser }) => {
if (!currentUser.isAuthenticated) {
return (
<div className="home-hero">
<h1>You must sign in or sign up in order to vote for matchups and view comments.</h1>
</div>
);
}
return (
<div>
<div className="home-hero">
<GameDetails />
</div>
</div>
)
}
export default GamePage;
And GameDetails.js is where I'm trying to get the id from the url to use in my redux action:
import React, { Component } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { getGameDetails } from "../store/actions/games";
class GameDetails extends Component {
componentDidMount() {
const game_id = match.params.game_id;
this.props.getGameDetails(game_id);
}
render() {
const { match, game } = this.props;
return (
<div className="home-hero">
<div className="offset-1 col-sm-10">
<h4>You are viewing the Game Page for game.title</h4>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
game: state.game
};
}
export default connect(mapStateToProps, { getGameDetails })(
GameDetails
);
I'm still very new to react and redux, so I appreciate any help you can offer.
Thank you for your time and patience!
Try something like this in GameDetails.js and GamePage.js
import React from "react";
import { Link } from "react-router-dom";
import GameDetails from "../containers/GameDetails";
const GamePage = ({ currentUser, ...props }) => {
if (!currentUser.isAuthenticated) {
return (
<div className="home-hero">
<h1>You must sign in or sign up in order to vote for matchups and view comments.</h1>
</div>
);
}
return (
<div>
<div className="home-hero">
<GameDetails {...props} />
</div>
</div>
)
}
export default GamePage;
import React, { Component } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { getGameDetails } from "../store/actions/games";
class GameDetails extends Component {
componentDidMount() {
const {game_id}= this.props.match.params.game_id;
this.props.getGameDetails(game_id);
}
render() {
const { match, game } = this.props;
return (
<div className="home-hero">
<div className="offset-1 col-sm-10">
<h4>You are viewing the Game Page for game.title</h4>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
game: state.game
};
}
export default connect(mapStateToProps, { getGameDetails })(
GameDetails
);
I suppose you use you exported component same thing like
<Route path="/games/:game_id" component={GameDetails} />
mapStateToProps get 2 arguments state, and ownProps.
function mapStateToProps(state, ownProps) {
const { match: { params: { game_id } } } = ownProps; // you able to use game_id inside mapStateToProps
return ({ game: state.game });
}
1 solution
import React from "react";
import { Link } from "react-router-dom";
import GameDetails from "../containers/GameDetails";
const GamePage = ({ currentUser, ...routeProps }) => {
if (!currentUser.isAuthenticated) {
return (
<div className="home-hero">
<h1>You must sign in or sign up in order to vote for matchups and view comments.</h1>
</div>
);
}
return (
<div>
<div className="home-hero">
<GameDetails {...routeProps} />
</div>
</div>
)
}
export default GamePage;
2 solution
import React, { Component } from "react";
import { connect } from "react-redux";
import { Link, withRouter } from "react-router-dom";
import { getGameDetails } from "../store/actions/games";
class GameDetails extends Component {
componentDidMount() {
const game_id = match.params.game_id;
this.props.getGameDetails(game_id);
}
render() {
const { match, game } = this.props;
return (
<div className="home-hero">
<div className="offset-1 col-sm-10">
<h4>You are viewing the Game Page for game.title</h4>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
game: state.game
};
}
export default withRouter(
connect(mapStateToProps, { getGameDetails })(GameDetails)
);

React: Passing data from between components via Route

I have a child component:
import * as React from 'react';
import Select from 'react-select';
import { Link } from 'react-router-dom';
import { Button } from '../controls/Button/Button';
import { ISelectedItem } from '../../interfaces/ISelectedItem';
import * as service from "../../helpers/service";
export interface IProps{
onClickRender: (selectedItem: ISelectedItem) => void;
}
export interface IState {
customerData: ISelectedItem[];
selectedItem: ISelectedItem;
}
export class DropDownSearch extends React.Component<{}, IState>{
constructor(props: any) {
super(props);
this.state = ({
customerData: [],
selectedItem: { shortName: '', description: '' }
});
}
componentDidMount() {
service.fetchJson<ISelectedItem[]>("/api/customers")
.then((json) =>{
this.setState({
customerData: json
});
});
}
handleChange = (selectedItem: any) => {
this.setState({
selectedItem
});
}
render() {
const { selectedItem } = this.state;
const value = selectedItem && selectedItem;
return (
<div>
<Select
name="form-field-name"
value={this.state.selectedItem}
onChange={this.handleChange}
options={this.state.customerData}
labelKey="shortName"
/>
<Link to={{
path "/dashboard/" + this.state.selectedItem.shortName,
state: { detail : this.state.selectedItem }
}}>
<Button type="button" className="btn btn-primary" caption="Search" />
</Link>
</div>
);
}
}
I want to pass the this.state.selectedItem to the Dashboard component, which is part of the Route config in the parent component below:
import * as React from 'react';
import { Navbar } from './Navbar/Navbar';
import { ShortNameSelector } from './ShortNameSelector/ShortNameSelector';
import { Dashboard } from './Dashboard/Dashboard';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
export class App extends React.Component<{},{}>{
render(){
return(
<BrowserRouter>
<div className="container">
<Navbar />
<div className="col-lg-12">
<Switch>
<Route exact path="/" component={ShortNameSelector} />
<Route path="/dashboard" component={Dashboard} />
</Switch>
</div>
</div>
</BrowserRouter>
);
}
}
Problem is I'm using Routes to switch components on the button click in my child components. How do I pass the this.state.selectedItem object from child to the Dashboard component (shown in parent component) via Routes?
EDIT:
So I put the state attribute inside Link tag and referenced it in Dashboard component like this.props.location.state.detail and it works. But now I want to persist the data in that route/Dashboard component when I open that link in a new page. How do I go about it?
You can use like this
<Route path="/dashboard/:selectedItem" component={Dashboard} />
So you can dynamically update the selected item in the DOM URL and when you click it, you can use 'this.props.match.params.id' in the 'Dashboard' component to access that value.
Passing object between components via Router in React: I have copied fragment of codes from my project, might be useful to you.
I use NavLink, which supposed pass an object to my InfoComponent
<NavLink to={{
pathname: /menu/${props.data.code},
search: '',
state: { selectedMenu: props.data }
}} color="info" className="btn btn-info btn-success mx-4">Info</NavLink>
In my router, I then received the passed argument in Router as follows, added console log for more clarity
<Route path="/menu/:item" render={(props) => {
console.log("::::::::: " + JSON.stringify(props.location.state.selectedMenu));
return (<InfoComponent selectedMenu={props.location.state.selectedMenu} />);
}} />

Clickable routing without link tag in react js

I want to navigate to another page when that component is clicked so i used to do that easily with Link tag but i want to navigate without link tag . is there any routing concepts rather that this Link tag . can someone clarify me pls .Here i attached my code ,
// Libraries
import React, { Component, Button, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Link } from 'react-router-dom'
import {Row} from './dataTables/Row';
class Customers extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.customersActions.fetchCustomers();
}
render() {
return (
<div className="customer-container">
<div className="body-container">
<div className="row-scroll">
{this.props.customersData.customers.filter(function(customer, index) {
if(index != 0) return true;
else return false;
}).map(customer =>
<Link to={'/customer/'+ customer.customer_id} key={customer.customer_id} className="line-removal">
<Row customer={customer} />
</Link> // what are all the other options instead of Link ???
)
}
</div>
</div>
</div>
);
}
}
function mapStateToProps(state, ownProps) {
return { customersData: state.customers };
}
export default connect(
})
)(Customers);
above is my code , can someone pls help me out from this.
You can use history.push() and standard react onClick to build the same behaviour like <Link /> provides.
Working demo (click on "Go to About" button): https://codesandbox.io/s/ER23MMvL0
Home.js
import React from 'react';
export default ({ history }) => {
const handleClick = () => {
history.push('/about');
};
return (
<div>
<h1>Home</h1>
<button onClick={handleClick}>Go to About</button>
</div>
);
};
index.js
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import Home from './Home';
import About from './About';
const styles = {
fontFamily: 'sans-serif',
textAlign: 'center',
};
const App = () => (
<Router>
<div style={styles}>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<hr/>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
</div>
</Router>
);
render(<App />, document.getElementById('root'));
You can use useNavigate() in Router v6.
import { useNavigate } from "react-router-dom";
const Home = () => {
const navigate = useNavigate();
<button onCLick={() => navigate("/about")}>Click me!</button>;
};

React router changing landing view to search results view

EDIT: Added component to view
I am trying to render the correct page view based on a submit button from a search. Currently I have a search bar at the top of the view and a default landing page in the middle. When the user searches I want to change the default landing page to the profile page they are searching for.
I am assuming I will have to remove the component from Main and replace it with {this.props.children}. Then in I will have to add maybe a around the submit button? The problem with this so far is that Profile then doesn't get the necessary props it needs from SearchBar.
My view ideally will show at the top and in the main container. When the user searches will change to containing the correct user information searched for which is passed to from -> ->
Below are my current Routs and Main components
import React, { Component } from 'react';
import { Router, Route, Redirect, IndexRoute, Link, hashHistory } from 'react-router';
import Main from '../components/Main';
import Profile from '../components/Profile';
import Landing from '../components/Landing';
class Routes extends Component {
render() {
return (
<Router history={ hashHistory }>
<Route path="/" component={Main}>
<Route path="Profile" component={Profile}></Route>
<Route path="Landing" component={Landing}></Route>
<IndexRoute component={Landing}></IndexRoute>
</Route>
</Router>
)
}
}
export default Routes;
Main
import React, { Component } from 'react';
import Routes from '../utils/Routes';
import Footer from './Footer';
import Profile from './Profile';
import SearchBar from './SearchBar';
import Landing from './Landing';
class Main extends Component {
constructor(props) {
super(props);
this.state = {
profileName: ''
}
}
handleProfileChange(profileName) {
this.setState( { profileName });
//replace <Profile /> with {this.props.children} maybe
}
render() {
return (
<div className="container-fluid">
<div className="row">
<SearchBar history={this.props.history} handleProfileChange={this.handleProfileChange.bind(this)} />
</div>
<div className="row">
<Profile name={this.state.profileName} />
</div>
<div className="row">
<Footer />
</div>
</div>
)
}
}
export default Main;
SearchBar
import React, { Component, PropTypes } from 'react';
import Profile from './Profile';
import TopNav from './TopNav';
import sass from '../scss/application.scss';
import { Router, Route, Redirect, IndexRoute, Link, hashHistory } from 'react-router';
class SearchBar extends Component {
constructor(props){
super(props)
this.state = {
name: ''
}
}
handleChange(e) {
this.setState({
name: e.target.value
});
}
handleSubmit(e) {
e.preventDefault();
console.log("searching for NAME " + this.state.name);
let profileName = this.state.name;
profileName = profileName.toLowerCase().trim();
//Cap the first letter in the name and add the rest of the name
profileName = profileName.charAt(0).toUpperCase() + profileName.substr(1);
console.log("NEW NAME " + profileName);
this.props.handleProfileChange(profileName);
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit.bind(this)}>
<input type="text" placeholder="Enter Name"
name="name"
value={this.state.name}
onChange={this.handleChange.bind(this)} />
<button className="btn btn-success" type="submit">Search</button>
</form>
</div>
)
}
}
SearchBar.propTypes = {
handleProfileChange: React.PropTypes.func.isRequired,
}
export default SearchBar;
Here's a basic sketch, if I understand you correctly. This would be the search bar and the submit.
class SearchProfiles extends Component {
static contextTypes = {
router: PropTypes.object,
};
state = { search: '' };
onSubmit = e => {
e.preventDefault();
// maybe do your search here, or inject it directly.
this.context.router.push(`/profiles/${this.state.search}`);
};
onChange = e => this.setState({ search: e.target.value });
render() {
return (
<form onSubmit={this.onSubmit}>
<input onChange={this.onChange} value={this.state.search} />
<button type="submit">Search Profiles</button>
</form>
)
}
}
You would have to add a route for the search, though.
<Route path="/profiles/:search" component={Profile} />

Resources