I need to put the change children with some element like div in react component. and I found lot of way to do that.
The problem is when I create a holder element over the child and give right key to the holder but the react added some weird character to the key and change my key.
I want to use react-grid-layout but the keys should be the same of the layout config props. so, I need to react stop changing the keys.
Here is my code:
class Main extends React.Component {
render() {
const { index, children } = this.props;
// Sample1: /.$
const childWithHolders1 = React.Children.map(children, child => (
<div key={"holder" + child.key}>
holder key: {"holder" + child.key}
</div>
));
const realReactKeys1 = childWithHolders1.map(divComp => divComp.key + "\n");
// Sample2: .$ (just pass number to the key)
const childWithHolders2 = React.Children.map(children, child => (
<div key={child.key}>
holder key: {child.key}
</div>
));
const realReactKeys2 = childWithHolders2.map(divComp => divComp.key + "\n");
return (
<div>
<h2>Sample 1</h2>
{childWithHolders1}
<br />
<h3>The Real React Keys: </h3>
<pre>{realReactKeys1}</pre>
<br /><h2>Sample 2</h2>
{childWithHolders2}
<br />
<h3>The Real React Keys: </h3>
<pre>{realReactKeys2}</pre>
</div>
);
}
}
class Child extends React.Component {
render() {
const { index } = this.props;
return (
<p>Child Index: {index}</p>
)
}
}
class App extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
<h2>What is weird "/.$" or ".$" in the react key name ???</h2>
<Main>
<Child key="1" index="1" />
<Child key="2" index="1" />
<Child key="3" index="1" />
</Main>
</div>
)
}
}
ReactDOM.render(<App />, document.querySelector("#root"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Related
I'm looking for a way to reduce code redundancy through class extending/inheritance on JavaScript/react.js.
For example, I want to have two types of UserCard components, that both represent a user's info, but one is for detailed (normal) view, other one is for list (mini) view. (e.g. imagine the normal one is something you may see on /user/:id and mini one is on /users)
Specifically, I wanted to have, for the normal ones, 1. its username and bio, icon, etc 2. its latset posts 3. extra actions (e.g. following/DM), and for the mini ones, exclude the 2. and 3. from the normal UserCard.
To implement the above model I thought I should use a JS extends or something (I'm not very familiar with extends), so basically I tried something similar to the following.
I know the following doesn't work, but I really don't have a good idea to how. What should I do with the issue? Thanks.
class UserCardBase extends Component {
constructor(props) {
super(props);
this.state = {
page_size: 3,
newly_order: true,
};
};
componentDidMount() {
{/* gets the user data */ }
axios.get(`/api/users/${this.props.id}`)
.then((response) => { this.setState({ user: response.data, updated: true, }); })
.catch((error) => { console.log(error); toast.error(() => <>failed.</>); });
};
render() {
const renderBlogcards = (props) => {
{/* renders blogcards */ }
return this.state.posts?.map((post, i) => <BlogCard key={i} data={post} />)
}
const extraActions = (props) => {
{/* enables follow/message */ }
return <div className="card__user-actions">
<button className="card__user-actions-follow">Follow</button>
<button className="card__user-actions-message">Message</button>
</div>
}
// mini sized usercard
const ContentMini = (props) => (
<>
<div className="__user_card">
<div className="card" >
<main className="card__user">
<img src={this.state.user?.userprofile.avatar} alt="" className="card__user-image"></img>
<div className="card__user-info">
<Link to={`/user/${this.state.user?.id}`}><h2 className="card__user-info__name">#{this.state.user?.username}</h2></Link>
<p className="card__user-info__desc">{this.state.user?.userbio.bio}</p>
</div>
</main>
</div>
</div>
</>
)
// default sized usercard
const Content = (props) => (
<>
<div className="__user_card">
<div className="card" >
<main className="card__user">
<img src={this.state.user?.userprofile.avatar} alt="" className="card__user-image"></img>
<div className="card__user-info">
<Link to={`/user/${this.state.user?.id}`}><h2 className="card__user-info__name">#{this.state.user?.username}</h2></Link>
<p className="card__user-info__desc">{this.state.user?.userbio.bio}</p>
</div>
<extraActions />
</main>
<renderBlogcards />
</div>
</div>
</>
)
return (
<>
{/* by default, uses the mini usercard */ }
<ContentMini />
</>
);
}
}
class UserCard extends UserCardBase {
constructor(props) {
super(props);
};
render() {
return (
<>
{/* assume trying overrides the render so uses the normal usercard replacing the mini */ }
<Content />
</>
)
}
}
I'd like to add a new input everytime the plus icon is clicked but instead it always adds it to the end. I want it to be added next to the item that was clicked.
Here is the React code that I've used.
const Input = props => (
<div className="answer-choice">
<input type="text" className="form-control" name={props.index} />
<div className="answer-choice-action">
<i onClick={props.addInput}>add</i>
<i>Remove</i>
</div>
</div>
);
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = {
choices: [Input]
};
}
addInput = index => {
this.setState(prevState => ({
choices: update(prevState.choices, { $splice: [[index, 0, Input]] })
}));
};
render() {
return (
<div>
{this.state.choices.map((Element, index) => {
return (
<Element
key={index}
addInput={() => {
this.addInput(index);
}}
index={index}
/>
);
})}
</div>
);
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"));
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
I must admit this get me stuck for a while but there was a problem with how react deals with key props. When you use an index as a key it doesn't work. But if you make sure inputs will always be assigned the same key even when the list changes it will work as expected:
const Input = props => (
<div className="answer-choice">
<input type="text" className="form-control" name={props.index} />
<div className="answer-choice-action">
<i onClick={props.addInput}>add </i>
<i>Remove</i>
</div>
</div>
);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
choices: [],
nrOfElements: 0
};
}
addInput = index => {
this.setState(prevState => {
const choicesCopy = [...prevState.choices];
choicesCopy.splice(index, 0, `input_${prevState.nrOfElements}`);
return {
choices: choicesCopy,
nrOfElements: prevState.nrOfElements + 1
};
});
};
componentDidMount() {
this.addInput(0);
}
render() {
return (
<div>
{this.state.choices.map((name, index) => {
return (
<Input
key={name}
addInput={() => {
this.addInput(index);
}}
index={index}
/>
);
})}
</div>
);
}
}
Some reference from the docs:
Keys should be given to the elements inside the array to give the
elements a stable identity...
...We don’t recommend using indexes for keys if the order of items may
change. This can negatively impact performance and may cause issues
with component state.
I'm trying to understand how the code below, which is from Redux examples TODOMVC, can be written using the class notation.
The code is
const App = ({todos, actions}) => (
<div>
<Header addTodo={actions.addTodo} />
<MainSection todos={todos} actions={actions} />
</div>
I tried the following but it doesn't work, I get Warning: App(...): When calling super() inApp, make sure to pass up the same props that your component's constructor was passed.
class App extends React.Component {
constructor({todos, actions}) {
super({todos, actions});
this.todos = todos;
this.actions = actions;
}
render() {
return(
<div>
<Header addTodo={this.actions.addTodo} />
<MainSection todos={this.todos} actions={this.actions} />
</div>
)
}
}
Whatever is passed to App is props. And ({ todos, actions }) is just destructuring from props. This should work:
class App extends React.Component {
render() {
const { todos, actions } = this.props;
return(
<div>
<Header addTodo={actions.addTodo} />
<MainSection todos={todos} actions={actions} />
</div>
)
}
}
By setting this.todo = todos in constructor, you're setting an instance level property. Which means if the props changes later, Header and MainSection will not be updated.
You can simply do what React asks, pass the whole props to the superclass and get out the properties you want explicitly
class App extends React.Component {
constructor(props) {
super(props);
this.todos = props.todos;
this.actions = props.actions;
}
render() {
return(
<div>
<h1>Actions: {this.actions}</h1>
{/*<Header addTodo={this.actions.addTodo} />
<MainSection todos={this.todos} actions={this.actions} />*/}
</div>
)
}
}
ReactDOM.render(<App todos={[]} actions={'some action'} />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
I have a React component MoviesGallery.js with the following configuration:
class MoviesGallery extends Component {
constructor(props) {
super(props)
this.state = { currentImage: 0 };
this.closeLightbox = this.closeLightbox.bind(this);
this.openLightbox = this.openLightbox.bind(this);
this.gotoNext = this.gotoNext.bind(this);
this.gotoPrevious = this.gotoPrevious.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState({movies_genre: nextProps.movies_genre})
}
I have rendered the component in my main App.js file like so:
class App extends Component {
render() {
return (
<MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
<RaisedButton primary={true} label="Query" className="header_buttons"/>
<RaisedButton secondary={true} label="Reset" className="header_buttons"/>
</header>
<MoviesGallery/>
</div>
</MuiThemeProvider>
);
}
}
I want to update the props of my MoviesGallery component without recreating the component. Since I already added the componentWillReceiveProps() to MoviesGallery component, how can I make it so when 'Query' button is clicked, it will pass new props to the already rendered MoviesGallery and componentWillReceiveProps() should cause it to re-render since the state will change.
Just confused about the function that will change the props themselves on-click of the rendered MoviesGallery component.
Thanks in advance!
When a parent pass a new (value) prop to the child, the child component will call the render method automatically. There is no need to set a local state inside the child component to "store" the new prop.
Here is a small example of a Counter that receives a count prop and just displays it, while the parent App in this case will change the value in its state and pass the new value to Counter:
class Counter extends React.Component {
render() {
const { count } = this.props;
return (
<div>{count}</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
onClick = () => {
this.setState({ count: this.state.count + 1 });
}
render() {
const { count } = this.state;
return (
<div>
<Counter count={count} />
<button onClick={this.onClick}>Add to counter</button>
</div>);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
you can use the 'state' for your MovieGallery.js props because the state is an object that changes and you must your code like below :
class App extends Component {
state = {
query : null
}
myFunction(query){
this.setState({query});
}
render() {
return (
<MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
<RaisedButton primary={true} label="Query" className="header_buttons" onClick={this.myFunction = this.myfunction.bind(this)}/>
<RaisedButton secondary={true} label="Reset" className="header_buttons"/>
</header>
<MoviesGallery newProps = {this.state.query}/>
</div>
</MuiThemeProvider>
);
}
}
i hope it helps
I have a component built using the below code. The aim is to add a class on the card to highlight it when the button inside it is clicked. However, the below code works on the first click but doesn't work for the subsequent clicks.
I understood that I have to set the clicked state of other elements to false when I remove the class. How can this be done?
import React, { Component } from 'react';
import './PricingCard.css';
class PricingCard extends Component {
constructor(){
super();
this.state = {
clicked : false
}
}
makeSelection(){
let elems = document.getElementsByClassName('Card');
for(var i=0;i<elems.length;i++){
elems[i].classList.remove("active");
}
this.setState({clicked: true});
}
render() {
var activeClass = this.state.clicked ? 'active' : '';
return (
<div className= {"categoryItem Card " + this.props.planName + " " +activeClass}>
<div className="cardDetails">
<div> {this.props.planName} </div>
<div className="pricing"> {this.props.price} </div>
<button onClick={this.makeSelection.bind(this)} className="buttonPrimary"> Select this plan </button>
<div className="subtitle"> {this.props.footerText} </div>
</div>
</div>
);
}
}
export default PricingCard;
Wouldn't it be easier to have the logic in a parent component? Since it is "aware" of all the child Card components.
Have something like...
this.state = { selectedComponent: null };
onClick(card_id) {
this.setState({ selectedComponent: card_id });
}
...in render:
const cards = smth.map((card) =>
<Card onClick={this.onClick.bind(this, card.id)}
isActive={map.id === this.state.selectedComponent} />
Would this work?
Best way will be to lift lift the state up. Like this:
class PricingCardContainer extends React.Component {
constructor(props){
super(props);
this.state = {
selectedCard: NaN,
}
}
handleCardClick(selectedCard){ this.setState({ selectedCard }); }
render() {
return (
<div>{
this.props.dataArray.map((data, i) =>
<PricingCard
key={i}
className={this.state.selectedCard === i ? 'active': ''}
price={data.price}
onClick={() => this.handleCardClick(i)}
footerText={data.footerText}
planName={data.planName}
plan={data.plan}
/>
)
}</div>
)
}
}
const PricingCard = ({ className = '', planName, price, onClick, footerText }) => (
<div className= {`categoryItem Card ${planName} ${className}`}>
<div className="cardDetails">
<div> {planName} </div>
<div className="pricing"> {price} </div>
<button onClick={onClick} className="buttonPrimary"> Select this plan </button>
<div className="subtitle"> {footerText} </div>
</div>
</div>
);
export default PricingCard;
Although it would be better to use some data id than index value.