What is the correct way in reactjs? - reactjs

I am new to react. In my project, I have a Home Component.
class Home extends Component {
logOut(){
console.log('log out in side home js');
this.props.LogOutAction();
localStorage.setItem('loginFlag', null);
}
render(){
return(
<div>
<Header />
<br/>
<div className="col-md-12 ">
<div className="col-md-3 home-border">
a
</div>
<div className="col-md-6 home-border">
<User/>
</div>
<div className="col-md-3 home-border">
c
</div>
</div>
</div>
)
}
}
And my User component is
class User extends React.Component {
viewProfile(id){
}
render() {
var userdetails = [ {"id":"1", "name":"Abraham", "country": "USA", "age":"29", "image":""},
{"id":"2", "name":"Gregory", "country": "Canada", "age":"23", "image":""},
{"id":"3", "name":"Mathews", "country": "Newzeland", "age":"24", "image":""},
{"id":"4", "name":"Williamson", "country": "China", "age":"27", "image":""},
{"id":"5", "name":"Edwerd", "country": "Germany", "age":"22", "image":""}
];
var myStyle = {
border:"1px solid black",
height:"170px",
}
return(
<div>
<div>
{userdetails.map((data, i) => <div className="col-md-12 div-bottom" style={myStyle} key={i}>
<div className="col-md-12">
<div className="col-md-12">
<img className="img-thumbnail img-margin" width="150" height="236" src = {'profile.jpg'} />
</div>
<div className="col-md-12 div-bottom"><label>{data.name}</label></div>
<div className="col-md-12 div-bottom">
<div className="col-md-6"><label>age:</label> {data.age}</div>
<div className="col-md-3"><label>Country:</label> {data.country}</div>
</div>
<div className="col-md-4 .div-bottom"><button className="btn btn-primary" onClick={ () => { this.viewProfile(data.id) } }>view Profile</button></div>
</div>
</div>)
}
</div>
</div>
)
}
}
I want to show the more user details in place of 'c' in the Home Component on the button click in the User Component.What is the correct method and how it can done?. Thanks in advance..

There are two approaches. First is to use React only. You create a parent component, containing the state of the app. In the parent component you can create a function that changes the state. You can pass the function to your user component and the state to the home component.
Simplified:
const Home = ({ user }) => <div>{user.name}</div>;
const User = ({ changeUser }) => {
const users = [
{ name: 'User1' },
{ name: 'User2' },
];
const renderUsers = () => users.map(
user => <li onClick={() => changeUser(user)}>{user.name}</li>
);
return <ul>{renderUsers()}</ul>;
};
class Parent extends React.Component {
constructor(props) {
super(props);
this.changeUser = this.changeUser.bind(this);
this.state = { user: null };
}
changeUser(user) {
this.setState({ user });
}
render() {
return (
<div>
<Home user={this.state.user} />
<User changeUser={this.changeUser} />
</div>
);
}
}
However, when your app grows it becomes very complicated to manage the state via a parent component. You can use something like a "Flux" architecture for this. "Redux" is the most common way in the React world for building up Flux like architecture.

What does 'a' stand for?
First of all , who does query you user's data?
I think Home must handle server calls, then you need to store that data in the Home component's state, then you need to pass them as props to the User and UserDetails component.
In my opinion Flux or Redux are needed only if your application become really big, and you don't want to have to many server calls during the session.
As a tips, if your User and UserDetails components only need to visualize data you can try to make them 'stateless' (refer to stateless components), in than way you don't need to handle willReceiveProps.
You can Handle 'viewProfile' method in the Home component, calling it from the User component with a callback (passed as a prop)

you can send an event as props and call it from other component.
Say you have a class
Class Home {
handleChange(evt) {
this.setState({ username: evt.target.value });
}
render {
return (
<div>
<Users name={this.state.username} onChange={this.handleChange}/>
<div> {this.state.username} </div>
</div>
);
}
}
Child Component
Class Users {
handleChange() {
//logic
}
render {
return (
<div>
<input type="text" onChange={this.props.onChange}/>
{this.props.name}
</div>
);
}
}
Here in Component Users when you change the input it will call the
method of class Home and update state of Home .
Now getting the updated state as props in component Users will give
you the changed text that you just entered

Related

Accesing object using props in ReactJs

I'm trying to access object keys using props as an index but it's not working. Error: Objects are not valid as a React child (found: object with keys {id, name, img_url, location}). If you meant to render a collection of children, use an array instead.
I am new to React so I appreciate any help.
My code:
class ExpandCard extends React.Component {
render() {
const props = this.props;
const profiles = props.profiles;
return(
<>
<div className="">
{profiles[props.active]}
</div>
</>
);
}
}
class App extends React.Component {
state = {
profiles: testData,
active: null,
}
getActive = (dataFromCard) => {
console.log('the magic number is', dataFromCard);
this.setState({active: dataFromCard});
}
render() {
return (
<div>
<div className="wrapper">
<header>
<div className="logo">LOGO</div>
</header>
<Form />
<div className="cards">
<div className="card-list">
{this.state.profiles.map(profile => <Card key={profile.id} {...profile} activeCard={this.getActive} />)}
</div>
<div className="expand-card">
<ExpandCard active={this.state.active} profiles={this.state.profiles} />
</div>
</div>
</div>
</div>
);
}
}
It looks like {profiles[props.active]} returns an object that looks like this:
{ id, name, img_url, location }
You can't return an object from a React component, maybe you meant to return {profiles[props.active].name}?

How to render reusable component one at a time in reactjs?

I reuse the Chat component twice into another component. It display when you click Chat button but it overlaps each other.
class Chat extends React.Component {
constructor() {
super();
this.state = {
show: false,
};
}
reset = () => {
this.setState(false);
}
open = () => {
this.setState({ show: true });
}
close = () => this.setState({ show: false });
render() {
return (<div className="chat">
<button className="btn-yes round" onClick={this.open}>{this.props.title}</button>
{this.state.show &&
<div className="show-chat">
<div className="chat-head">Now Chatting <i className="fas fa-angle-down" onClick={this.close}></i></div>
<div className="chat-body">
<div className="blue">Teresa wants to chat about her healthcare finances</div>
<ul>
<li><img src={agentPhoto} alt="chat agent avatar" /></li>
<li>
<h6>John Newman</h6>
<div className="gray">Hi Teresa!</div>
<div className="gray">Here is the link to the finance tool we discussed.</div>
<div className="gray">If you have any questions, let me know!</div>
</li>
</ul>
</div>
<input placeholder="Type here and hit enter to chat"></input>
</div>}
</div>);
}
}
I expect to display chat one at a time. When I click the Chat button 2 and the Chat 1 is displayed, Chat 1 should be hidden.
Essentially, you need to give each Chat component an identifier and keep track of the one that is currently opened.
Here is the basic structure for your Parent component:
class App extends React.Component {
state = {
currentChatId: null
};
handleOpen = id => {
this.setState({
currentChatId: id
});
};
render() {
return (
<div>
<Chat
identifier={1}
currentChatId={this.state.currentChatId}
handleOpen={this.handleOpen}
/>
<Chat
identifier={2}
currentChatId={this.state.currentChatId}
handleOpen={this.handleOpen}
/>
</div>
);
}
}
So notice, we give each Chat component an identifier prop. We will use identifier to update the active chat - which we stored as a value called currentChatId in our parent-state. That is all done through the handleOpen() event-handler, which we also pass down as a prop to Chat.
Now in your Chat component, we need to configure logic for open() and componentDidUpdate()
class Chat extends React.Component {
constructor() {
super();
this.state = {
show: false
};
}
componentDidUpdate(prevProps) {
const { identifier, currentChatId } = this.props;
if (this.props.currentChatId !== prevProps.currentChatId) {
this.setState({
show: identifier === currentChatId ? true : false
});
}
}
open = () => {
const { identifier, handleOpen } = this.props;
handleOpen(identifier);
};
render() {
return (
<div className="chat">
<button className="btn-yes round" onClick={this.open}>
{this.props.title}
</button>
{this.state.show && (
<div className="show-chat">
<div className="chat-head">
Now Chatting{" "}
<i className="fas fa-angle-down" onClick={this.close} />
</div>
<div className="chat-body">
<div className="blue">
Teresa wants to chat about her healthcare finances
</div>
<ul>
<li>
<img src={""} alt="chat agent avatar" />
</li>
<li>
<h6>John Newman</h6>
<div className="gray">Hi Teresa!</div>
<div className="gray">
Here is the link to the finance tool we
discussed.
</div>
<div className="gray">
If you have any questions, let me know!
</div>
</li>
</ul>
</div>
<input placeholder="Type here and hit enter to chat" />
</div>
)}
</div>
);
}
}
Workflow:
User clicks one of the Chat buttons, triggering handleOpen()and we
pass in the unique identifier....
That gets passed back up to the Parent, and now currentChatId
should be updated with the identifier...
That currentChatId gets passed back down to the Chat component as the
currentChatId prop...
Triggers componentDidUpdate() on all Chat components, and we check
the currentChatId against their own identifiers, only one will be
matching, so we display that one.
See codesandbox for working example: https://codesandbox.io/s/react-example-kgm2h

react-redux: Rendering a component after an API call

I am building an app which uses user input and shows number of recipes and they can click on recipe card to view ingredients as well. Every time they click on recipe card I make an API call to get appropriate recipe ingredient. But I am not able to figure out how to show the component which contains the recipe ingredients. I tried with conditional routing and conditional rendering as well but couldn't find the solution.
Recipe_Template.js
export class RecipeTemplate extends Component {
renderRecipe = recipeData => {
return recipeData.recipes.map(recipeName => {
return (
<div className="container">
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<a
href={recipeName.source_url}
target="_blank"
onClick={() => {
this.props.fetchRecipeId(recipeName.recipe_id);
}}
>
<img
src={recipeName.image_url}
className="mx-auto d-block img-fluid img-thumbnail"
alt={recipeName.title}
/>
<span>
<h3>{recipeName.title}</h3>
</span>
</a>
<span}>
<h3>{recipeName.publisher}</h3>
</span>
</div>
</div>
</div>
);
});
};
render() {
return (
<React.Fragment>
{this.props.recipe.map(this.renderRecipe)}
</React.Fragment>
);
}
}
Recipe_Detail.js
class RecipeDetail extends Component {
renderRecipeDetail(recipeData) {
return recipeData.recipe.ingredients.map(recipeIngredient => {
return <li key={recipeIngredient}>recipeIngredient</li>;
});
}
render() {
if (this.props.recipeId === null) {
return <div>Loading...</div>;
}
return <ul>{this.props.recipeId.map(this.renderRecipeDetail)}</ul>;
}
}
function mapStateToProps({ recipeId }) {
return { recipeId };
}
export default connect(mapStateToProps)(RecipeDetail);
Not entirely sure why you would need Redux here (unless it's being shared among other nested components), but I'm fairly certain you can just utilize React state.
One approach would be to configure your routes as such:
<Route path="/recipes" component={Recipes} />
<Route path="/recipe/:id" component={ShowRecipe} />
When the user sends a query, gets some results, and you display all matching recipes to a Recipes component. Each recipe then has a name (and other associated displayable data) and a clickable link:
<Link to={`/recipe/id?recipeId=${recipeId}`}>View {recipeName} Recipe</Link>
which for simplicity sake might look like:
<ul>
<Link to="/recipe/id?recipeId=08861626">View Prosciutto Bruschetta Recipe</Link>
<Link to="/recipe/id?recipeId=04326743">View Pasta Bundt Loaf Recipe</Link>
...etc
</ul>
When the user clicks on the link, react-router sends the user to the ShowRecipe component with a unique recipeId.
ShowRecipe then makes another AJAX request to get the recipe details:
ShowRecipe.js
export default class ShowRecipe extends Component {
state = { recipeDetail: '' }
componentDidMount = () => {
const { recipeId } = this.props.location.query; // <== only natively available in react-router v3
fetch(`http://someAPI/recipe/id?recipeId=${recipeId}`)
.then(response => response.json())
.then(json => this.setState({ recipeDetail: json }));
}
render = () => (
!this.state.recipeDetails
? <div>Loading...</div>
: <ul>
{this.state.recipeDetail.map(ingredient => (
<li key={ingredient}>ingredient</li>
)}
</ul>
)
}
Another approach:
Have the recipeDetails stored and available within the original fetched recipes JSON. Then map over the recipes and create multiple <Card key={recipeId} recipeName={recipeName} recipeDetail={recipeDetail} /> components for each recipe.
which for simplicity sake might look like:
<div>
{this.state.recipes.map(({recipeId, recipeName, recipeDetail}), => (
<Card key={recipeId} recipeName={recipeName} recipeDetail={recipeDetail} />
)}
</div>
Then each individual Card has it's own state:
Card.js
export default class Card extends Component {
state = { showDetails: '' }
toggleShowDetails = () => this.setState(prevState => ({ showDetails: !this.state.showDetails }))
render = () => (
<div>
<h1>{this.props.recipeName} Recipe</h1>
<button onClick={toggleShowDetails}> {`${!this.state.showDetails ? "Show" : "Hide"} Recipe<button>
{ this.state.showDetails &&
<ul>
{this.props.recipeDetail.map(ingredient => (
<li key={ingredient}>ingredient</li>
)}
</ul>
}
)
}
Therefore, by default the recipeDetail is already there, but hidden. However, when a user clicks the Card's button, it will toggle the Card's showDetails state to true/false to display/hide the recipe detail.

React:Not able to access the component form data into another component

I have two components one is Home component and Category component. First its redirecting to Home page use of routing.
In home page ,I have two fields, username and password. I filled the username and password and click on submit, its redirecting to category page.
I want access the username and password fields in category component.
I am not able to access those fields in category component.
render() {
return (
<Router>
<div>
<nav className="navbar navbar-default">
<div className="container-fluid">
<div className="navbar-header">
<a className="navbar-brand" href="#">WebSiteName</a>
</div>
<ul className="nav navbar-nav">
<li>
<Link className="active" to="/">Home</Link>
</li>
<li>
<Link to="/category">Category</Link>
</li>
</ul>
</div>
</nav>
<hr />
<Route exact path="/" component={Home} />
<Route path="/category" component={Category} />
</div>
</Router>
)
render() {
return (
<form >
<label>
Name:
<input type="text" name="username" value={this.state.username} onChange = {(event) => this.setState({username:event.target.value})}/>
</label>
<br/>
<label>
password:
<input type="text" name="password" value={this.state.password} onChange = {(event) => this.setState({password:event.target.value})}/>
</label>
<br/>
<input class="btn btn-primary" type="submit" onClick={(event) => this.handleClick(event)} value="Submit" />
</form>
);
}
}
constructor(props) {
super(props);
console.log("props", this.props);
console.log("props....", props)
}
clickForSettings() {
this.props.history.push('/settings');
}
render() {
return (
<div>
<h1>This is Category Page{this.props.username}</h1>
</div>
);
}
This is by design. Read up on this article on data storage in react, and if you decide you still want to do it the way you're operating, you'll want to do the below:
First, put the variables you want in the lowest common "parent" of the two classes you want to share data. For you, this sounds like the class where you have your router.
Second, create a function that alters the variables, just like you would've in your home controller. For you, this would probably be these two functions:
changeUserName(userName){ this.setState({ userName: userName }) }
changePassword(password){ this.setState({ password: password }) }
Third, bind those functions to the "parent" component, so that this is always pointing to the parent's this (this must be done in the constructor of the "parent"):
this.changeUserName = this.changeUserName.bind(this);
this.changePassword = this.changePassword.bind(this);
Fourth, send the functions down into Home, and the variables down into Category using timdorr's solution here.
Fifth, call the method in the input onChange functions in the Home class:
onChange(event) { this.props.changeUserName(event.target.value) }
Now you should be able to see the changes on your Category component with this.props.userName or however you sent it down to your Category component.
Sample of the method outlined above
export default class App extends Component {
constructor() {
this.state = {
userName:""
}
this.changeUserName = this.changeUserName.bind(this);
}
changeUserName(userName) {
this.setState({userName});
}
render() {
return (
<div>
<Login userName={this.state.userName} changeUserName={this.changeUserName} />
<NeedsUserName userName={this.state.userName} />
</div>
)
}
}
export default class Login extends Component {
changeUserName(event) {
this.props.changeUserName(event.target.value);
}
render() {
<input onChange={(event) => this.changeUserName(event)} value={this.props.userName}/>
}
}
export default class NeedsUserName extends Component {
render() {
return (
<h1>{this.props.userName}</h1>
)
}
}

How to push values into state by calling single onChange function - react

I am new to reactive. I am working on react+flux+alt with ES6.
I have a form for creating new record.
Component
import React from 'react';
import { Input, Button, Glyphicon, ButtonToolbar } from 'react-bootstrap';
import AttributeSectionStore from 'stores/attributeSection/AttributeSectionStore';
import TextBoxesSet from '../descriptionTextBoxes';
import styles from 'scss/_common';
export default class AttributeSection extends React.Component {
constructor(props) {
super(props);
}
_onCreate = () => {
console.log('___________', this.state);
}
onChangeName = (evt) => {
this.setState({name: evt.target.value});
};
onChangeRank = (evt) => {
this.setState({rank: evt.target.value});
};
static getPropsFromStores() {
return recordStore.getState();
}
render() {
return (
<div className="container">
<div className={styles.mainheader}>
<h2 >New Record</h2>
</div>
<div className="col-md-9">
<form className="form-horizontal">
<div className="row">
<div className="col-md-12">
<Input type="text" label="Name" labelClassName="col-xs-2"
wrapperClassName="col-xs-4" value={this.props.name}
onChange={this.onChangeName}/>
</div>
</div>
<div className="row">
<div className="col-md-12">
<Input type="number" label="Rank" labelClassName="col-xs-2"
wrapperClassName="col-xs-4" value={this.props.rank}
onChange={this.onChangeRank}/>
</div>
</div>
<div className="row">
<div className="col-md-4 col-md-offset-2">
<ButtonToolbar className={styles.formBtnGrp}>
<Button bsStyle="primary" onClick={this._onCreate}>Create</Button>
<Button type="reset">Cancel</Button>
</ButtonToolbar>
</div>
</div>
</form>
</div>
</div>
);
}
}
AttributeSection.propTypes = {
name: React.PropTypes.string
rank: React.PropTypes.number
};
Using above component now I'm getting data into state but form may have more than 2 fields. I'm using two functions to update state instead of that how can use single function to update state object?Is there any other best practice is there?
The most common pattern to solve this is using bind() to curry a value to the onchange callback. This is was #knowbody referenced (React.js: Identifying different inputs with one onChange handler)
An alternate, but similar, pattern is adding a second tag within the element to identify the name of the state property to change. I'll show an example using label from your code (obviously you want to use a dedicated tag since label is for display and would be localized).
onInputChanged(evt) {
var newState = this.state,
propName = evt.target.label.toLowerCase();
newState[propName] = evt.target.value;
this.setState(newState);
};

Resources