Reputation: 33
We are starting to work on a project as 3 students who are new to react. I wanted to split the code so everyone could work on something at once. But when I tried to use a state from icon-bar.js in App.js I got error messages. App.js
import React, {Component} from 'react';
import './App.css';
import IconBar from './components/icon-bar';
import Events from './components/events';
function App() {
return (
<div className="App">
<h1 className="Title">ENAKS</h1>
<IconBar />
<Events />
<div className="mainModeFrame"><p1>{this.state.mode}</p1></div>
</div>
);
}
export default App;
icon-bar.js
import React, {Component} from 'react';
import '../App.css';
class IconBar extends Component {
constructor() {
super()
this.state = {
mode: 'Hi'
}
}
render() {
return(
<div className="icon-bar">
<div className="iconHolder">
<img src="/images/2dlogo.png" alt="" className="icons" onClick={() => this.setState({mode: '2d'})}/>
<img src="/images/3dlogo.png" alt="" className="icons" onClick={() => this.setState({mode: '3d'})}/>
<img src="/images/flag.png" alt="" className="icons" onClick={() => this.setState({mode: 'edit'})}/>
<img src="/images/tracker.png" alt="" className="icons" onClick={() => this.setState({mode: 'track'})}/>
<div>{this.state.mode}</div>
</div>
</div>
)
}
}
export default IconBar;
How can I use my "mode" state to change my p1 in the App.js
Upvotes: 3
Views: 20915
Reputation: 11830
There are two ways to do this.
The first one is to use something like redux. where you have global store for the state which can be shared across different components.
The second one would be to emit the state in the parent component. Here Icon bar should be child component to your parent component app.js
. From parent component you pass function as a props and that function should take an argument which could be state
Below Icon bar takes props now (see constructor and super)
import React, {Component} from 'react';
import '../App.css';
class IconBar extends Component {
constructor(props) { //
super(props)
this.state = {
mode: 'Hi'
}
}
render() {
return(
<div className="icon-bar">
<div className="iconHolder">
<img src="/images/2dlogo.png" alt="" className="icons" onClick={() => this.setState({mode: '2d'})}/>
<img src="/images/3dlogo.png" alt="" className="icons" onClick={() => this.setState({mode: '3d'})}/>
<img src="/images/flag.png" alt="" className="icons" onClick={() => this.setState({mode: 'edit'})}/>
<img src="/images/tracker.png" alt="" className="icons" onClick={() => this.setState({mode: 'track'})}/>
<div>{this.state.mode}</div>
</div>
</div>
)
}
}
export default IconBar;
and then in app.js
import React, {Component} from 'react';
import './App.css';
import IconBar from './components/icon-bar';
import Events from './components/events';
function App() {
const getStateFromChild = state => {
// body of function
// return something
}
return (
<div className="App">
<h1 className="Title">ENAKS</h1>
<IconBar upsideEmit={getStateFromChild} />
<Events />
<div className="mainModeFrame"><p1>{this.state.mode}</p1></div>
</div>
);
}
export default App;
When do you want the state to be sent to the parent component depends on you.
[Update:] I do something very similar in one of my open source repo. instead of navigating through the entire code, look at this line
Here I have a parent component, In which I have child component Checkboxes
<CheckBoxes
options={options}
upsideEmit={onChangeHandler}
defaultColor={defaultColor}
/>
CheckBoxes is passed a function onChangeHandler upsideEmit={onChangeHandler}
(ignore the body of the function)
const onChangeHandler = (data, errorMessage=null) => {
// Error Message is usually passed from the child components (look ad date componenet example )
if (!errorMessage && (data === '' || data)) { // Doing data = "" in case someone want to remove last char of a string
const currentData = {...payload}
currentData[key] = data
setPayloadData(currentData)
} else if (errorMessage) {
setErrorData({status: true, message: errorMessage})
}
return
}
Now, If you look at this line in checkboxes component, I am passing my child state to parent component
const toggleState = (index) => {
let copyState = [...state]
const previousState = copyState[index]['value']
if (multipleSelect) {
// Setting all other values as false, since the array for checkbox won't in normal scenario be big, we can just iterate and set it to false
copyState = copyState.map(element => {
element.value = false
return element
})
}
copyState[index]['value'] = !previousState
setState(copyState)
upsideEmit(copyState)
}
If this helps, please give the repo a star.
Upvotes: 1
Reputation: 1625
Finding the correct component to hold your state is important. Here you want to move your mode to the parent component state and access it from child component as a prop.
I see your child component is setting mode which is a little tricky since it will now be held in the parent state, so how to do that ?
You create a callback in the parent and set that callback as a prop when you call your component in the parent render.
You will then need to set your onClick event handler on each image element to call an event handler (in this case handleClick) in your child component.
This event handler will then grab the id of the target element and pass that to the parent using this.props.onUpdateMode (the callback that we passed as a prop).
The parent callback receives the data from the child, and updates state accordingly.
Reference: Lifting State Up
import React, {Component} from 'react';
import './App.css';
import IconBar from './components/icon-bar';
import Events from './components/events';
class App() extends Component {
constructor(props) {
super(props)
this.state = {
mode: null
}
this.updateMode = this.updateMode.bind(this);
}
updateMode = (newMode) => {
this.setState({mode: newMode});
}
return (
<div className="App">
<h1 className="Title">ENAKS</h1>
<IconBar onUpdateMode={this.updateMode} mode={this.state.mode} />
<Events />
<div className="mainModeFrame"><p1>{this.state.mode}</p1></div>
</div>
);
}
export default App;
import React, {Component} from 'react';
import '../App.css';
class IconBar extends Component {
constructor(props) {
super(props)
this.handleClick=this.handleClick.bind(this);
}
handleClick(e) {
let mode = e.target.id;
this.props.onUpdateMode(mode);
}
render() {
return(
<div className="icon-bar">
<div className="iconHolder">
<img src="/images/2dlogo.png" id="2d" alt="" className="icons" onClick={this.handleClick} />
<img src="/images/3dlogo.png" id="3d" alt="" className="icons" onClick={this.handleClick}/>
<img src="/images/flag.png" id="edit" alt="" className="icons" onClick={this.handleClick}/>
<img src="/images/tracker.png" id="track" alt="" className="icons" onClick={this.handleClick}/>
<div>{this.props.mode}</div>
</div>
</div>
)
}
}
export default IconBar;
Upvotes: 3