I'm trying to update more than 5 components' status after a button is clicked. I'm now putting all the status and event handlers in App.js. For example,
// MyButton.js
export const MyButton = (props) => {
<button type='button' onClick={props.onClick}>Click Me!</button>
}
// Component1.js
export const Component1 = ({ name }) => {
return (
<h1>{name}</h1>
);
}
// Component2.js
export const Component2 = ({ name }) => {
return (
<h1>{name}</h1>
);
}
App.js
class App extends React.Component {
constructor(props) {
super(props);
this.state = { name1: '', name2: '' };
this.changeNames = this.changeNames.bind(this);
}
changeNames() {
this.changeComponent1Name();
this.changeComponent2Name();
}
changeComponent1Name() {
this.setState({ name1: 'name1' });
}
changeComponent2Name() {
this.setState({ name2: 'name2' });
}
render() {
return (
<MyButton onClick={this.changeName} />
<Component1 name={this.state.name1} />
<Component2 name={this.state.name2} />
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
The code becomes more complicated as more components I have to update when the button is clicked. I'd like to put the status and event handlers in each components so the code can be more readable. Any suggestions?
just loop over state:
changeNames() {
let newState = {...this.state}
for (var stateProp in newState) {
//add here your logic depending on prop name/for every prop
}
}
Related
In reactjs I want to update parent state based on child state.
My parent component in Login.
I want child state in Login component role function
//this function to show link //
function GetLink(props) {
const { role } = props
let admin = <Link to='/Admin'>Admin</Link>
let f = <Link to='/Finance'>Finance</Link>
let s = <Link to='/Sales'>Sales</Link>
switch (role) {
default:
case "admin": return (
<>
{admin}
{f}
{s}
</>
)
case "finance": return (
<>
{f}
{s}
</>
)
case "sales": return (
<>
{s}
</>
)
}
}
//this is the parent component //
class Login extends Component {
constructor(props) {
super(props);
this.state = {
role: ""
}
}
//want this state to be update when child state is updated
role = () => {
this.setState({ role: });
}
render() {
return (
<>
{ this.state.role === "admin" }
<GetLink role={localStorage.getItem("role")} />
</>
);
}
}
now this is my child component where the state is updating in componentDidMount
//this is child component //
//the state is updating in this component //
class Sales extends Component {
constructor(props) {
super(props);
this.state = {role: "" }
}
componentDidMount() {
if (localStorage.getItem("role") === null) {
this.props.setState({ role: localStorage.setItem('role', 'sales') })
}
}
logout() {
localStorage.removeItem('role');
}
render() {
return (
<>
<h1>Sales</h1>
<button onClick={this.logout}>logout</button>
</>
);
}
}
export default Sales;
can anyone help me out with this problem?
I think it is the best way that you send a function as props.
<GetLink role={this.role} />
I have a child component which fires function pageChange when I press 2 button in the pagination bar:
import React from "react";
import "antd/dist/antd.css";
import { Pagination } from "antd";
const Child = props => {
function pageChange(pageNumber) {
props.pageChange(pageNumber);
console.log("Page child: ", pageNumber);
}
return (
<div>
<Pagination
defaultCurrent={1}
total={50}
onChange={pageChange}
/>
</div>
);
};
export default Child;
This child is used in the Parent class component:
import React, { Component } from "react";
import Child from "./Child";
class Parent extends Component {
state = {
pageIndex: 3
};
componentDidMount() {
this.getSetData(this.state.pageIndex);
}
getSetData(pageNumber) {
this.setState({
pageIndex: 5
});
console.log("Page state: ", this.state.pageIndex);
console.log("pageNumber: ", pageNumber);
}
pageChange(pageNumber) {
console.log("Page parent: ", pageNumber);
this.getSetData(pageNumber);
}
render() {
return (
<div className="container">
<Child pageChange={this.pageChange} />
</div>
);
}
}
export default Parent;
But I have troubles with it:
When I press 2 button I get an error this.getSetData is not a function. How to call getSetData from pageChange?
Statement this.setState({pageIndex: 9}); do not works during componentDidMount call. Why?
SOLVED
As far as I understand a function without an arrow has its own this. So one must use
pageChange = pageNumber => {
this.getSetData(pageNumber);
};
instead of
pageChange(pageNumber) {
this.getSetData(pageNumber);
}
setState is async, so to see change in console is useful to call from render
render() {
console.log("render ", this.state.pageIndex);
return (
<div className="container">
<Child pageChange={this.pageChange} />
</div>
);
}
An easy solution can be, using ES6 functions. So you don't need to bind that in constructor
getSetData = (pageNumber) => {
this.setState({
pageIndex: 5
});
console.log("Page state: ", this.state.pageIndex);
console.log("pageNumber: ", pageNumber);
}
EDIT 1
Try using your child component like this.
<Child pageChange={() => this.pageChange()} />
Running Example
EDIT 2
<Child pageChange={(e) => this.pageChange(e)} />
You didn't pass the parameter to pagechange function that's why it didn't recieve anything.
Try this it will surely work. I have tested this in your code.
May be you need to bind the function with this to use this.setState.
See here for more
class Parent extends Component {
constructor(props) {
super(props);
// binding here
this.state = {
pageIndex: 3
};
this.getSetData = this.getSetData.bind(this);
this.getSetDataArrow = this.getSetDataArrow.bind(this);
this.pageChange = this.pageChange.bind(this);
}
state = {
// items: [{ a: 1, b: 2 }, { a: 3, b: 4 }],
pageIndex: 3
};
componentDidMount() {
this.getSetDataArrow(this.state.pageIndex);
this.getSetData(this.state.pageIndex);
}
getSetData(pageNumber) {
this.setState({
pageIndex: 5
});
console.log("Page state: ", this.state.pageIndex);
console.log("pageNumber: ", pageNumber);
}
getSetDataArrow = pageNumber => {
this.setState({
pageIndex: 4
});
console.log("Page state: ", this.state.pageIndex);
console.log("pageNumber: ", pageNumber);
};
pageChange(pageNumber) {
console.log("Page parent: ", pageNumber);
// this.setState({
// pageIndex: 9
// });
// this.setState is not a function
this.getSetDataArrow(pageNumber);
this.getSetData(pageNumber);
// this.getSetData is not a function
}
render() {
return (
<div className="container">
<Child pageChange={this.pageChange} />
{/* <Child pageChange={this.pageChange} items={this.state.items} /> */}
{/* <Child2 pageChange={this.pageChange} /> */}
</div>
);
}
}
export default Parent;
I am Trying to Create a React App That Detects Age of Pictures Using Clarifai API .
I am Able to Console.Log Detected Age but I Want To Display The Age on My Webpage . Help me With Setting The AgeDectect State so I Can Use it on my Webpage
//Value Displayed On Console
//39
//App.js Code That Console.Logs Age
class App extends Component {
constructor(){
super();
this.state = {
input : '',
imgURL : '',
AgeDetect : ''
}
}
onInputChange = (event) => {
this.setState({input : event.target.value});
}
onClickEvent = () => {
this.setState({imgURL : this.state.input})
app.models.predict(Clarifai.DEMOGRAPHICS_MODEL ,
this.state.input).then(
function(response) {
const A =response.outputs[0].data.regions[0].
data.face.age_appearance.concepts[0].name
//This Line of Code Displays Age on Console
console.log(A);
this.setState({AgeDetect : A});
},
//Having Problem SettingState ,this.state.AgeDetect isnt
//doing anything
render(){
return (<AgeDetection AgeDetect={this.state.AgeDetect}/>
)
}
//AgeDetection.js file
import React from 'react' ;
const AgeDetection = ({AgeDetect}) => {
return(
<div>
{AgeDetect}
</div>
);
}
export default AgeDetection;
Sort your array that is returned by the value and set the first object or the whole array to your state and then you can use it in your app very easily. Use an arrow function inside your predict then block to bind to the class.
class App extends Component {
constructor() {
super();
this.state = {
input: '',
imgURL: '',
AgeDetect: ''
};
}
onInputChange = event => {
this.setState({ input: event.target.value });
};
onClickEvent = () => {
this.setState({ imgURL: this.state.input });
app.models.predict(Clarifai.DEMOGRAPHICS_MODEL, this.state.input).then(
response => {
const A =
response.outputs[0].data.regions[0].data.face.age_appearance
.concepts[0].name;
this.setState({ AgeDetect: A });
},
function(err) {
// there was an error
}
);
};
render() {
console.log(this.state);
return (
<div className='App'>
<Navigation />
<Logo />
<ImageLinkForm
onInputChange={this.onInputChange}
onClickEvent={this.onClickEvent}
/>
<FaceRecognition imgURL={this.state.imgURL} />
<AgeDetection AgeDetect={this.state.AgeDetect} />
</div>
);
}
}
export default App;
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>
);
}
I have a parent class-based component A and a child functional component B. Inside B I map over a list of names and render them as li elements, which onClick call the onLanguageUpdate handler declared in the parent component, and what this handler does is update the state to reflect the selected name.
Question then:
I need to call a second event handler in the same onClick, this time to change the color of the name the user has clicked on. I added a new property to the state, color, to represent a className that I can then toggle with the handleStyleColorChange handler. But how do I get the li elements in the child component to update their className (or style) based on the result of this handler? If I was doing all of this inside component A's render method, I could do style={language === this.state.selectedLanguage ? {color: 'red'} : null} on the li and call it a day.
// Component A
import React, { Component } from 'react';
import B from './B';
class A extends Component {
constructor(props) {
super(props);
this.state = {
selectedLanguage: 'All',
color: 'lang-black-text'
};
}
handleUpdateLanguage = (language) => {
return this.setState({ selectedLanguage: language });
}
handleStyleColorChange = (language) => {
if (language === this.state.selectedLanguage) {
return this.setState({ color: 'lang-red-text' });
} else {
return this.setState({ color: 'lang-black-text' });
}
}
handleClick = (language) => {
this.handleUpdateLanguage(language);
this.handleStyleColorChange(language);
}
render() {
return (
<LanguageList onLanguageUpdate={this.handleClick} />
);
}
}
export default A;
// Component B
import React from 'react';
const B = (props) => {
const languages = ['English', 'Spanish', 'Japanese', 'Italian'];
const languageListFormatted = languages.map(language => {
return (
<li
key={language}
onClick={() => props.onLanguageUpdate(language)}>{language}
</li>
);
});
return (
<ul className="languages">{languageListFormatted}</ul>
);
}
export default B;
You can't manage the color from the parent comp, it needs to be done from the child comp. Then, send the selectedLanguage to the child and you are good.
class A extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedLanguage: 'All',
color: 'lang-black-text'
};
}
handleUpdateLanguage = (language) => {
return this.setState({ selectedLanguage: language });
}
handleStyleColorChange = (language) => {
if (language === this.state.selectedLanguage) {
return this.setState({ color: 'lang-red-text' });
} else {
return this.setState({ color: 'lang-black-text' });
}
}
handleClick = (language) => {
this.handleUpdateLanguage(language);
this.handleStyleColorChange(language);
}
render() {
return (
<B
onLanguageUpdate={this.handleClick}
selectedLanguage={this.state.selectedLanguage}
/>
);
}
}
const B = (props) => {
const languages = ['English', 'Spanish', 'Japanese', 'Italian'];
const languageListFormatted = languages.map(language => {
return (
<li
key={language}
style={props.selectedLanguage === language ? {background: 'yellow'} : {}}
onClick={() => props.onLanguageUpdate(language)}>{language}
</li>
);
});
return (
<ul className="languages">{languageListFormatted}</ul>
);
}
ReactDOM.render(
<A />,
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>