How to pass props through Link method in React? - javascript

I am trying to pass a prop from one component in which I search for and select a game to another component where I will render the details of the selected game. I am keeping my components as two separate pages, but I am struggling to get anything passing down to the child component. Here are my two files, and I have no idea where I am going wrong.
import React, { Component } from "react";
import Selected from "./Selected";
import { Link } from 'react-router-dom';
class Search extends Component {
constructor(props) {
super(props);
this.state = {
/*
API request format:
GET https://api.rawg.io/api/platforms?key=YOUR_API_KEY
GET https://api.rawg.io/api/games?key=YOUR_API_KEY&dates=2019-09-01,2019-09-30&platforms=18,1,7
Docs: https://api.rawg.io/docs
*/
baseURL: "https://api.rawg.io/api/games?",
apiKey: `key=${process.env.REACT_APP_RAWG_API_KEY}&`,
gamesQuery: "search=",
searchInput: "",
// later on we can determine whether to add additional parameters like page size, genres, etc.
searchURL: "",
gallery : [],
selectedGame: [],
};
}
handleChange = (event) => {
this.setState({
// we're grabbing the element or elements and dynamically setting the input value to the key corresponding to the input id of the same name in this.state
[event.target.id]: event.target.value,
});
};
handleSubmit = (event) => {
// keep the page from refreshing on submit
event.preventDefault();
this.setState(
{
// builds out our search url from the pieces we've assembled
searchURL:
this.state.baseURL +
this.state.apiKey +
this.state.gamesQuery +
this.state.searchInput,
},
() => {
// we fetch the url from the api
fetch(this.state.searchURL)
// .then waits till the fetch is complete
.then((response) => {
return response.json();
})
.then(
(json) => this.setState({
gallery : json.results
}),
(err) => console.log(err)
);
}
);
};
handleInspect = (event) => {
for (let i in this.state.gallery) {
if (i.id === event.id) {
this.setState ({
selectedGame : i
})
}
}
}
render() {
let game;
if (this.state.selectedGame) {
game = this.state.selectedGame
}
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>Search</label>
<input
id="searchInput"
type="text"
placeholder="What's the Name of the Game"
value={this.state.searchInput}
onChange={this.handleChange}
/>
<input type="submit" value="Find Games" />
</form>
<div id='gallery'>
{this.state.gallery.map(function(d, idx){
return (
<li key={idx}>
<a href={"/selected/"+d.id}
onClick={()=>this.handleInspect(d.id)}
>{d.name}</a>,
{d.id},
<Link to={{pathname: `/selected/${d.id}`,
gameResults : game}} />,
</li>)})}
</div>
</div>
);
}
}
export default Search;
And the component I try to pass to and fails.
import React from 'react';
import { Link } from 'react-router-dom';
class Selected extends React.Component {
render() {
{console.log(this.props)}
return (
<h1>woo</h1>
);
}};
export default Selected;
The result is below, with no props having been passed at all

Related

React Initiating component re-render after onChange event received data from Paste (ctrl+v)

I'm trying to initiate an API request upon paste of a URL into an input field and then show the result on the page.
According to documentation and this link on SOF, setState is the way to initiate re-render, I know and it seems I did it the right way myself, but something is off, I get the url state only when I do onChange again, React doesn't seem to show me my pasted data anywhere in any of the available lifecycle events.
Using create-react-app:
import React from "react";
import ReactDOM from "react-dom";
const UserInput = props => {
return (
<div>
<label>Enter URL:</label>
<input onChange={props.handleChange} type="text" value={props.value} />
</div>
);
};
class Fetch extends React.Component {
constructor() {
super();
this.state = {
url: null,
userData: null,
fetching: false,
error: null
};
}
componentDidUpdate() {
this.fetchData();
}
fetchData() {
fetch(this.state.url)
.then(result => result.json())
.then(json => this.setState({ userData: json }))
.error(error => console.log(error));
}
render() {
return this.props.render();
}
}
const UserProfile = ({ name, gender }) => {
return (
<div>
Hey {name}, you are {gender}!
</div>
);
};
class App extends React.Component {
constructor() {
super();
this.state = {
url: null
};
}
handleChange(e) {
this.setState({
url: e.target.value
});
}
render() {
return (
<div>
<UserInput
value={this.state.url}
handleChange={this.handleChange.bind(this)}
/>
<Fetch url={this.state.url} render={data => <UserProfile />} />
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
If you paste any URL in the field, you won't have it in state, so when fetchData is triggered its
this.state.url
is actually still null.
Thanks
Your Fetch component and App component are using two separate copies of the url state which causes the issue, you have to use the url you pass as prop to the Fetch component instead.
class Fetch extends React.Component {
constructor(props) {
super(props);
this.state = {
// url: null, remove this
userData: null,
fetching: false,
error: null
};
}
componentDidUpdate() {
this.fetchData();
}
fetchData() {
fetch(this.props.url) // update here
.then(result => result.json())
.then(json => this.setState({ userData: json }))
.error(error => console.log(error));
}
render() {
return this.props.render(userData); // the render prop is a function in your case that expects data
}
}
update the below line too so that the UserProfile gets the data that has been obtained from API. I am not sure about the keys
<Fetch url={this.state.url} render={data => <UserProfile name={data.name} gender={data.gender}/>} />

React - How to redirect to another component on Search?

So I'm working on an app for school and I've been stuck on an issue for like two days. I'm building a search that gets data from TMDB, and all that works fine. When I type in the input all the data come flowing in! However, when I submit and try to redirect to the /results page which is linked to a component that displays the SearchResults when directed to, nothing happens and it stays on the homepage... I tried Redirect but either I'm using it incorrectly, or I shouldn't be using it in this case. Here's the code for my Search component, if you could help me out, I'd greatly appreciate it!:
import React, { Component, Redirect } from 'react';
import axios from 'axios';
class Search extends Component {
state = {
query: '',
results: []
};
getInfo = () => {
axios
.get(
`https://api.themoviedb.org/3/search/tv?api_key=6d9a91a4158b0a021d546ccd83d3f52e&language=en-US&query=${
this.state.query
}&page=1`
)
.then(({ data }) => {
this.setState({
results: data
});
});
};
handleInputChange = e => {
e.preventDefault();
this.setState(
{
query: this.search.value
},
() => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query.length % 2 === 0) {
this.getInfo();
}
} else if (!this.state.query) {
}
}
);
};
render() {
return (
<div>
<form>
<input
className='search'
placeholder='⌕'
type='text'
ref={input => (this.search = input)}
onChange={this.handleInputChange}
/>
</form>
{this.state.results.length > 0 && (
<Redirect
to={{
pathname: '/results',
state: { results: this.state.results }
}}
/>
)}
</div>
);
}
}
export default Search;
You can use withRouter to get history prop injected and then do: history.push(“/results”)
The code will look something like this:
import React, { Component, Redirect } from 'react';
import axios from 'axios';
import { withRouter } from "react-router";
class Search extends Component {
state = {
query: '',
results: []
};
componentDidUpdate(prevProps, prevState) {
const { history } = this.props;
if (prevState.results !== this.state.results) {
history.push('/results');
}
}
getInfo = () => {
axios
.get(
`https://api.themoviedb.org/3/search/tv?api_key=6d9a91a4158b0a021d546ccd83d3f52e&language=en-US&query=${
this.state.query
}&page=1`
)
.then(({ data }) => {
this.setState({
results: data
});
});
};
handleInputChange = e => {
e.preventDefault();
this.setState(
{
query: this.search.value
},
() => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query.length % 2 === 0) {
this.getInfo();
}
} else if (!this.state.query) {
}
}
);
};
render() {
return (
<div>
<form>
<input
className='search'
placeholder='⌕'
type='text'
ref={input => (this.search = input)}
onChange={this.handleInputChange}
/>
</form>
</div>
);
}
}
export default withRouter(Search);
This way you are programmatically navigating, using push method from react router's history.
Here you can read more about withRouter HOC: https://reacttraining.com/react-router/web/api/withRouter
Are you using React Router for routing?. Do you need to change your URL? There isn't much of a need for something like this. Sending someone to /results is just old school unless you are going to do something like /results?q=pulp&piction so someone can refresh or link directly to the results.
For something like this just display your result component
{this.state.results.length && (
<SearchResults results={this.state.results} />
)}
If you are using a router and need to use the path for the school project school your teacher for dumb business requirements and give us more information about what you are using.
"Redirect" component is inside the "react-router-dom" package.
use this approach:
import { Redirect } from "react-router-dom";
import React, { Component } from 'react';
class Search extends Component {
//...
render() {
const { results } = this.state
const { props } = this.props
return (results.length > 0 ? <Redirect
to={{
pathname: "/results",
state: {
results: results,
from: props.location
}
}}
/> : <form>
<input
className='search'
placeholder='⌕'
type='text'
ref={input => (this.search = input)}
onChange={this.handleInputChange}
/>
</form>
)
}
}

adding data to a dictionary in react

I am using mqtt to receive some data and I would like to add that data directly to a dictionary.
My probably naive way to add the data to the dictionary is the following:
this.client.on('message', (topic, message) => {
this.state.mqttMessage[topic] = message.toString();
})
I also tried the following:
this.setState(prevState => ({
mqttMessage: {
...prevState.mqttMessage,
topic: message.toString()
}
}))
But that adds the keyword "topic" to the dictionary.
The above line seems to generate the following warning as well:
Do not mutate state directly. Use setState() react/no-direct-mutation-state
Later I would like to show what was received above, using this code:
render() {
console.log(this.props.mqttMessage)
return (
<div>
test1: {this.props.mqttMessage["test1"]} <br/>
test2: {this.props.mqttMessage["test2"]} <br/>
test3: {this.props.mqttMessage["test3"]} <br/>
</div>
);
}
But the problem is that objects don't seem to get updated directly, I need ti refresh the page in order for the content to kick in.
I guess I am not using the setState properly.
The proper way of doing what I am trying to do would be appreciated.
Below is the whole code:
import React, { Component } from 'react';
import Button from 'react-bootstrap/lib/Button';
import Tab from 'react-bootstrap/lib/Tab';
import Tabs from 'react-bootstrap/lib/Tabs';
import 'rc-slider/assets/index.css';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
mqttMessage: {}
}
this.mqtt = require('mqtt')
this.client = this.mqtt.connect('mqtt://192.168.10.100:9001')
this.client.on('connect', () => {
this.client.subscribe('test1')
this.client.subscribe('test2')
this.client.subscribe('test3')
})
this.client.on('message', (topic, message) => {
this.state.mqttMessage[topic] = message.toString();
})
}
render() {
return (
<div className="App">
<Tabs defaultActiveKey="profile" id="uncontrolled-tab-example">
<Tab eventKey="home" title="Home">
<PostMessage client={this.client} />
</Tab>
<Tab eventKey="seats" title="Seats">
<SeatPage mqttMessage={this.state.mqttMessage} client={this.client} />
</Tab>
</Tabs>
</div>
);
}
}
class PostMessage extends React.Component {
sendMessage = (e) => {
e.preventDefault();
this.props.client.publish('demo/test', 'My Message');
}
render() {
return (
<Button onClick={this.sendMessage}>
Send Message
</Button>
);
}
}
class SeatPage extends React.Component {
sendMessage = (e, topic, message) => {
e.preventDefault();
this.props.client.publish(topic, message);
}
render() {
console.log(this.props.mqttMessage)
return (
<div>
test1: {this.props.mqttMessage["test1"]} <br/>
test2: {this.props.mqttMessage["test2"]} <br/>
test3: {this.props.mqttMessage["test3"]} <br/>
</div>
);
}
}
export default () => (
<App />
);
In react you shouldn't change state object directly. You should rather use setState method.
Something like below should do the trick.
this.client.on('message', (topic, message) => {
this.setState({
...this.state,
mqttMessage: {
...this.state.mqttMessage,
[topic]: message
}
})
})

Axios get method response in React cannot be displayed getting data from firebase as an array in my blog application

I wonder if someone could help me. I have read many StackOverflow's answers around this and other great articles like this one and I couldn't implement an answer yet.
I have got a simple blog app in React. I have a form to submit the data and I have separate post and posts component as well. I can actually send data to my firebase database. I also get the response in GET method but I cannot show the response as I need it to be. I need an array of posts which each post has a title and content so that I can send its data to my Post component. But I always get an error like( map cannot be used on the response) and I actually cannot get an array out of my database. I even wonder if I am sending data in the right format. Please check my code below and help me out. Thanks.
// The individual post component
const Post = props => (
<article className="post">
<h2 className="post-title">{props.title}</h2>
<hr />
<p className="post-content">{props.content}</p>
</article>
);
// The form component to be written later
class Forms extends React.Component {}
// The posts loop component
class Posts extends React.Component {
state = {
posts: null,
post: {
title: "",
content: ""
}
// error:false
};
componentDidMount() {
// const posts = this.state.posts;
axios
.get("firebaseURL/posts.json")
.then(response => {
const updatedPosts = response.data;
// const updatedPosts = Array.from(response.data).map(post => {
// return{
// ...post
// }
// });
this.setState({ posts: updatedPosts });
console.log(response.data);
console.log(updatedPosts);
});
}
handleChange = event => {
const name = event.target.name;
const value = event.target.value;
const { post } = this.state;
const newPost = {
...post,
[name]: value
};
this.setState({ post: newPost });
console.log(event.target.value);
console.log(this.state.post.title);
console.log(name);
};
handleSubmit = event => {
event.preventDefault();
const post = {
post: this.state.post
};
const posts = this.state.posts;
axios
.post("firebaseURL/posts.json", post)
.then(response => {
console.log(response);
this.setState({ post: response.data });
});
};
render() {
let posts = <p>No posts yet</p>;
if (this.state.posts) {
posts = this.state.posts.map(post => {
return <Post key={post.id} {...post} />;
});
}
return (
<React.Fragment>
<form className="new-post-form" onSubmit={this.handleSubmit}>
<label>
Post title
<input
className="title-input"
type="text"
name="title"
onChange={this.handleChange}
/>
</label>
<label>
Post content
<input
className="content-input"
type="text"
name="content"
onChange={this.handleChange}
/>
</label>
<input className="submit-button" type="submit" value="submit" />
</form>
</React.Fragment>
);
}
}
class App extends React.Component {
render() {
return (
<React.Fragment>
<Posts />
</React.Fragment>
);
}
}
// Render method to run the app
ReactDOM.render(<App />, document.getElementById("id"));
And this is a screenshot of my firebase database:
My Firebase database structure
It is interesting that what I found is rarely mentioned anywhere around it.
This is the entire Posts component:
class Posts extends React.Component {
state = {
posts: [],
post: {
title: "",
content: ""
}
};
componentWillMount() {
const { posts } = this.state;
axios
.get("firebaseURL/posts.json")
.then(response => {
const data = Object.values(response.data);
this.setState({ posts : data });
});
}
handleChange = event => {
const name = event.target.name;
const value = event.target.value;
const { post } = this.state;
const newPost = {
...post,
[name]: value
};
this.setState({ post: newPost });
console.log(event.target.value);
console.log(this.state.post.title);
console.log(name);
};
handleSubmit = event => {
event.preventDefault();
const {post} = this.state;
const {posts} = this.state;
axios
.post("firebaseURL/posts.json", post)
.then(response => {
console.log(response);
const newPost = response.data;
this.setState({ post: response.data });
});
};
render() {
let posts = <p>No posts yet</p>;
if (this.state.posts) {
posts = this.state.posts.map(post => {
return <Post key={post.id} {...post} />;
});
}
return (
<React.Fragment>
{posts}
<form className="new-post-form" onSubmit={this.handleSubmit}>
<label>
Post title
<input
className="title-input"
type="text"
name="title"
onChange={this.handleChange}
/>
</label>
<label>
Post content
<input
className="content-input"
type="text"
name="content"
onChange={this.handleChange}
/>
</label>
<input className="submit-button" type="submit" value="submit" />
</form>
</React.Fragment>
);
}
}
Actually as I first time read in this question you should not rely on console.log to see if your posts (or your response data) has been updated. Because in componentDidMount() when you immediately update state you will not see the change in console. So what I did was to display the data that I got from the response using map over the posts and it showed my items as I actually had an array although couldn't see in the console. This is my code for componentDidMount:
axios.get("firebaseURL/posts.json").then(response => {
const data = Object.values(response.data);
this.setState({
posts: data
});
And show the posts:
let posts = <p>No posts yet</p>;
if (this.state.posts) {
posts = this.state.posts.map(post => {
return <Post key={post.id} {...post} />;
});
}
And it shows all the posts as expected. Take away is to be careful once woking on componentDidMound and other lifecycle methods as you might not see the updated data in the console inside them but you actually need to use it as it is in the response. The state is updated but you are not able to see it inside that method.
Not a database expert, but I believe your database is structured a bit odd and will only cause problems further down the line, especially when it comes to editing/updating a single post. Ideally, it should structured like a JSON array:
posts: [
{
id: "LNO_qS0Y9PjIzGds5PW",
title: "Example title",
content: "This is just a test"
},
{
id: "LNOc1vnvA57AB4HkW_i",
title: "Example title",
content: "This is just a test"
},
...etc
]
instead its structured like a JSON object:
"posts": {
"LNO_qS0Y9PjIzGds5PW": {
"post": {
"title": "Example title",
"content": "This is just a test"
}
},
"LNOc1vnvA57AB4HkW_i": {
"post": {
"title": "Example title",
"content": "This is just a test"
}
},
...etc
}
Anyway, your project should have a parent Posts container-component that controls all your state and fetching of data, then it passes down its state and class methods to component children. Then the children can update or display the parent's state accordingly.
OR
You should separate your Posts container-component, so that it either displays found posts or a "No posts found" component. And then, have your Posts Form component be it's own/unshared component whose only function is to show a form and submit it to a DB.
Up to you and what you think fits your needs.
Working example: https://codesandbox.io/s/4x4kxn9qxw (the example below has one container-component that shares with many children)
Note: If you change posts to an empty array [], instead of data in fetchData()s this.setState() function, you can have the PostForm be displayed under the /posts route!
ex: .then(({ data }) => this.setState({ isLoading: false, posts: [] }))
index.js
import React from "react";
import { render } from "react-dom";
import App from "./routes";
import "uikit/dist/css/uikit.min.css";
import "./styles.css";
render(<App />, document.getElementById("root"));
routes/index.js
import React from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Home from "../components/Home";
import Header from "../components/Header";
import Posts from "../containers/Posts";
export default () => (
<BrowserRouter>
<div>
<Header />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/posts" component={Posts} />
<Route path="/postsform" component={Posts} />
</Switch>
</div>
</BrowserRouter>
);
containers/Posts.js
import isEmpty from "lodash/isEmpty";
import React, { Component } from "react";
import axios from "axios";
import PostsForm from "../components/postsForm";
import ServerError from "../components/serverError";
import ShowPosts from "../components/showPosts";
import Spinner from "../components/spinner";
export default class Posts extends Component {
state = {
content: "",
error: "",
isLoading: true,
posts: [],
title: ""
};
componentDidUpdate = (prevProps, prevState) => {
// check if URL has changed from "/posts" to "/postsform" or vice-versa
if (this.props.location.pathname !== prevProps.location.pathname) {
// if so, check the location
this.setState({ isLoading: true }, () => this.checkLocation());
}
};
componentDidMount = () => this.checkLocation();
checkLocation = () => {
// if the location is "/posts" ...
this.props.location.pathname === "/posts"
? this.fetchData() // then fetch data
: this.setState({ // otherwise, clear state
content: "",
error: "",
isLoading: false,
posts: [],
title: ""
});
};
// fetches posts from DB and stores it in React state
fetchData = () => {
axios
.get("firebaseURL/posts.json")
.then(({ data }) => this.setState({ isLoading: false, posts: data }))
.catch(err => this.setState({ error: err.toString() }));
};
// handles postsForm input changes { content: value , title: value }
handleChange = e => this.setState({ [e.target.name]: e.target.value });
// handles postsForm form submission
handleSubmit = event => {
event.preventDefault();
const { content, title } = this.state;
alert(`Sumbitted values: ${title} - ${content}`);
/* axios.post("firebaseURL/posts.json", { post: { title, content }})
.then(({data}) => this.setState({ content: "", posts: data, title: "" }))
.catch(err => this.setState({ error: err.toString() }))
*/
};
// the below simply returns an if/else chain using the ternary operator
render = () => (
this.state.isLoading // if isLoading is true...
? <Spinner /> // show a spinner
: this.state.error // otherwise if there's a server error...
? <ServerError {...this.state} /> // show the error
: isEmpty(this.state.posts) // otherwise, if posts array is still empty..
? <PostsForm // show the postForm
{...this.state}
handleChange={this.handleChange}
handleSubmit={this.handleSubmit}
/>
: <ShowPosts {...this.state} /> // otherwise, display found posts!
);
}
components/postsForm.js
import React from "react";
export default ({ content, handleSubmit, handleChange, title }) => (
<form
style={{ padding: "0 30px", width: 500 }}
className="new-post-form"
onSubmit={handleSubmit}
>
<label>
Post title
<input
style={{ marginBottom: 20 }}
className="uk-input"
type="text"
name="title"
onChange={handleChange}
placeholder="Enter post title..."
value={title}
/>
</label>
<label>
Post content
<input
style={{ marginBottom: 20 }}
className="uk-input"
type="text"
name="content"
onChange={handleChange}
placeholder="Enter post..."
value={content}
/>
</label>
<button
disabled={!title || !content}
className="uk-button uk-button-primary"
type="submit"
>
Submit
</button>
</form>
);
components/showPosts.js
import map from "lodash/map";
import React from "react";
export default ({ posts }) => (
<div className="posts">
{map(posts, ({ post: { content, title } }, key) => (
<div key={key} className="post">
<h2 className="post-title">{title}</h2>
<hr />
<p className="post-content">{content}</p>
</div>
))}
</div>
);
components/serverError.js
import React from "react";
export default ({ err }) => (
<div style={{ color: "red", padding: 20 }}>
<i style={{ marginRight: 5 }} className="fas fa-exclamation-circle" /> {err}
</div>
);

Unmount component on click in child component button // React

I am struggling with successfully removing component on clicking in button. I found similar topics on the internet however, most of them describe how to do it if everything is rendered in the same component. In my case I fire the function to delete in the child component and pass this information to parent so the state can be changed. However I have no idea how to lift up the index of particular component and this is causing a problem - I believe.
There is a code
PARENT COMPONENT
export class BroadcastForm extends React.Component {
constructor (props) {
super(props)
this.state = {
numberOfComponents: [],
textMessage: ''
}
this.UnmountComponent = this.UnmountComponent.bind(this)
this.MountComponent = this.MountComponent.bind(this)
this.handleTextChange = this.handleTextChange.bind(this)
}
MountComponent () {
const numberOfComponents = this.state.numberOfComponents
this.setState({
numberOfComponents: numberOfComponents.concat(
<BroadcastTextMessageForm key={numberOfComponents.length} selectedFanpage={this.props.selectedFanpage}
components={this.state.numberOfComponents}
onTextChange={this.handleTextChange} dismissComponent={this.UnmountComponent} />)
})
}
UnmountComponent (index) {
this.setState({
numberOfComponents: this.state.numberOfComponents.filter(function (e, i) {
return i !== index
})
})
}
handleTextChange (textMessage) {
this.setState({textMessage})
}
render () {
console.log(this.state)
let components = this.state.numberOfComponents
for (let i = 0; i < components; i++) {
components.push(<BroadcastTextMessageForm key={i} />)
}
return (
<div>
<BroadcastPreferencesForm selectedFanpage={this.props.selectedFanpage}
addComponent={this.MountComponent}
textMessage={this.state.textMessage} />
{this.state.numberOfComponents.map(function (component) {
return component
})}
</div>
)
}
}
export default withRouter(createContainer(props => ({
...props
}), BroadcastForm))
CHILD COMPONENT
import React from 'react'
import { createContainer } from 'react-meteor-data'
import { withRouter } from 'react-router'
import { BroadcastFormSceleton } from './BroadcastForm'
import './BroadcastTextMessageForm.scss'
export class BroadcastTextMessageForm extends React.Component {
constructor (props) {
super(props)
this.handleChange = this.handleChange.bind(this)
this.unmountComponent = this.unmountComponent.bind(this)
}
handleChange (e) {
this.props.onTextChange(e.target.value)
}
unmountComponent (id) {
this.props.dismissComponent(id)
}
render () {
console.log(this.props, this.state)
const textMessage = this.props.textMessage
return (
<BroadcastFormSceleton>
<div className='textarea-container p-3'>
<textarea id='broadcast-message' className='form-control' value={textMessage}
onChange={this.handleChange} />
</div>
<div className='float-right'>
<button type='button'
onClick={this.unmountComponent}
className='btn btn-danger btn-outline-danger button-danger btn-small mr-3 mt-3'>
DELETE
</button>
</div>
</BroadcastFormSceleton>
)
}
}
export default withRouter(createContainer(props => ({
...props
}), BroadcastTextMessageForm))
I am having problem with access correct component and delete it by changing state. Any thoughts how to achieve it?
Please fix the following issues in your code.
Do not mutate the state of the component. Use setState to immutably change the state.
Do not use array index as the key for your component. Try to use an id field which is unique for the component. This will also help with identifying the component that you would need to unmount.
Try something like this. As mentioned before, you don't want to use array index as the key.
class ParentComponent extends React.Component {
constructor() {
this.state = {
// keep your data in state, as a plain object
textMessages: [
{
message: 'hello',
id: '2342334',
},
{
message: 'goodbye!',
id: '1254534',
},
]
};
this.handleDeleteMessage = this.handleDeleteMessage.bind(this);
}
handleDeleteMessage(messageId) {
// filter by Id, not index
this.setState({
textMessages: this.state.textMessages.filter(message => message.id !== messageId)
})
}
render() {
return (
<div>
{this.state.textMessages.map(message => (
// Use id for key. If your data doesn't come with unique ids, generate them.
<ChildComponent
key={message.id}
message={message}
handleDeleteMessage={this.handleDeleteMessage}
/>
))}
</div>
)
}
}
function ChildComponent({message, handleDeleteMessage}) {
function handleClick() {
handleDeleteMessage(message.id)
}
return (
<div>
{message.message}
<button
onClick={handleClick}
>
Delete
</button>
</div>
);
}

Categories