Onclick an element that changes the state of another element - reactjs

I'm trying to click on a sort icon that will trigger to change the order of a list.
To make it more simpler, let's say you have a button and another button and they are on separate divs from each other.
<div>
//Button 1
<button onclick={"some_click_handler"}>
</div>
<div>
//Button 2
<button>
{this.state.someToggle ? true : false}
</button>
</div>

Create a component which passes a callback to the button, this callback will update the state of the container which will in turn set the props of the list. This is very common in React and is the basis of how the compositional pattern works. If you need to share data between two components just put them in a container and lift the state to the parent component. These components are usually called containers and there is a bunch of documentation on it.
This is a good starting point: https://reactjs.org/docs/lifting-state-up.html
Something like this...
class Container extends React.Component {
constructor(props) {
super(props);
// Don't forget to bind the handler to the correct context
this.handleClick = this.handleClick.bind(this);
}
handleClick(sort) {
this.setState({sort: sort});
}
render() {
return (
<Button handleClick={this.handleClick} />
<List sort={this.state.sort} />
)
}
}

Related

Insert data using multiple components in reactjs

I am trying to insert data in database using multiple components in reactjs. In my first component I have a Form like below (there is not Submit button in this component)
render() {
return (
<form>
<input type="text" name="name" placeholder="Name"/>
</form>
);
}
In second component I am calling that component with if else logic.
In third component I have the submit button like below
<Button
positive icon='checkmark'
labelPosition='right'
onClick={this.insertAddress}
content='Save'
/>
My main issue is using state. How can I use state effectively here ? How can I catch the input values of first component in third component to insert in database ?
You create a parent container. The parent container holds the state, all the methods and event handlers. All of those are binding to the parent.
You then pass the methods and parts of the state down to the children. When they children use them, they will be changing the parent. I created a simple sandbox to demonstrate. You don't need to pass the entire state down, just the parts needed by the children.
https://codesandbox.io/s/q335wnjoyj
Use the concept of Container component.
create a container component which will hold the state of all you child components. keep updating all state related to Data in here. and call save functionality from here.
class Parent extends React.Component{
constructor(props){
super(props);
this.state={
name:'',
email:'',
phone:''
}
this.handleFormChanges = this.handleFormChanges.bind(this)
this.handleSubmit = this.handleSubmit.bind(this);
this.reactToSomeChange = this.reactToSomeChange.bind(this)
}
handleFormChanges(ev){
// handle form and set appropriate state
}
handleFormChanges(ev){
// react to change and set state
}
handleSubmit(){
// update your data store db etc.
}
render(){
return(
<MyForm changes={this.handleFormChanges} />
<IfElse changes={this.reactToSomeChange} />
<Button submit={this.handleSubmit} />
)
}
}
// MyFrom component render function
render() {
return (
<form>
<input type="text" name="name" onChanges={this.props.changes} placeholder="Name"/>
</form>
);
// Button component render function
render(){
<button onClick={this.props.submit}>SUBMIT<Button>
}
Child components need not to call apis to save the data. it should be done from a single parent component that shares the same state in the child components.

Is it okay to call setState on a child component in React?

I have some text. When you click on that element a modal pops up that lets you edit that text. The easiest way to make this work is to call setState on the child to initialise the text.
The other way, although more awkward, is to create an initial text property and make the child set it's text based on this.
Is there anything wrong with directly calling setState on the child or should I use the second method?
Although it is recommended to keep the data of your react application "up" in the react dom (see more here https://reactjs.org/docs/lifting-state-up.html), I don't see anything wrong with the first aproach you mentioned.
If you have to store data that is very specific of a child I don't see anything wrong in keep that information in the child's state.
It seems that your modal doesn't need to have its own state, in which case you should use a stateless React component.
This is one way of passing the data around your app in the React way.
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
initialText: "hello",
}
this.saveChildState = this.saveChildState.bind(this);
}
saveChildState(input) {
console.log(input);
// handle the input returned from child
}
render() {
return (
<div>
<ChildComponent
initialText={this.state.initialText}
save={this.saveChildState}
/>
</div>
);
}
}
function ChildComponent(props) {
return (
<div>
<input id="textInput" type="text" defaultValue={props.initialText}>
</input>
<button onClick={() => props.save(document.getElementById('textInput').value)}>
Save
</button>
</div>
)
}
Maybe I am misinterpreting your question, but I think it would make the most sense to keep the modal text always ready in your state. When you decide to show your modal, the text can just be passed into the modal.
class Test extends Component {
constructor() {
this.state = {
modalText: 'default text',
showModal: false
}
}
//Include some method to change the modal text
showModal() {
this.setState({showModal: true})
}
render(
return (
<div>
<button onClick={() => this.showModal()}>
Show Modal
</button>
{ this.state.showModal ? <Modal text={this.state.modalText}/> : null }
</div>
)
)
}

Component class not rendering

I'm having a weird problem with a self-made ReactJS component and I must be doing something fundamentally wrong.
I have a parent component, which periodically receives an array of objects by means of a REST service. The received elements are enumerated and put into a react-bootstrap ListGroup element:
<Row>
<Col sm={2}>
<ListGroup>
{this.state.maps.map((object, index) => {
return <ListGroupItem key={index}
onClick={() => this.setState({ currentMapSelection: object })}>
{object.map_name}
</ListGroupItem>
})}
</ListGroup>
</Col>
<Col sm={10}>
{this.renderMapForm()}
</Col>
</Row>
The display of the ListGroupItems is working fine.
Now I wanted to edit an List element by clicking on it. For code separation I wanted to put the entire edit logic into a separate component (MapLoadForm), which renders some Form elements and means to update and delete.
I put the rendering into a separate function, which conditionally renders the MapLoadForm:
renderMapForm() {
const mapForm = this.state.currentMapSelection;
if (mapForm != undefined) {
return <MapLoadForm map={mapForm}></MapLoadForm>
}
return <div></div>
}
The problem now is, that the MapLoadForm just renders correctly for the very first click. Subsequent clicks to other list items don't change it anymore. I don't see the call the the MapLoadForm constructor anymore.
If I replace the return <MapLoadForm map={mapForm}></MapLoadForm> by return <Button>{mapForm.map_name}</Button>, I see a new button rendered with every new click. The same happens, if I put the entire Form rendering logic into the return statement of the renderMapForm() function. Just the "outsourcing" into a new component doesn't work.
import React from 'react';
...
export class MapLoadForm extends React.Component {
constructor(props) {
super(props);
}
...
render() {
return (
<Form style={{ marginTop: "20px" }} horizontal>
...
</Form>
)
}
}
export default MapLoadForm;
Question now: Should that work or am I on the completely wrong track?
TIA
You are missing one part: Whenever props of any component changes, react re-render that component but re-rendering doesn't mean unmounting and mounting the component. It means same component will be rendered with new props, constructor will not get called again.
Put this console inside render method of MapLoadForm component, you will see new value each time component re-render:
console.log('props', this.props.map)
Solution:
What you are looking for is, componentWillReceiveProps lifecycle method, whenever component received new props, this lifecycle method will get called, do the calculation again with new props values, like this:
componentWillReceiveProps(nextProps){
console.log('new props', nextProps);
}
componentWillReceiveProps
componentWillReceiveProps() is invoked before a mounted component
receives new props. If you need to update the state in response to
prop changes (for example, to reset it), you may compare this.props
and nextProps and perform state transitions using this.setState() in
this method.

Changing background-image after onClick event in ReactJS

I have a component, let's say it looks like this:
return(
<div id="container"></div>
)
From the beginning it's background image is already set, let's say
#container{
background-image: url('./assets/container.jpg');}
Now I want to add another element inside the container div that will have onClick event firing function, that will do other things + changing the parent's background-image. Dummy code would be:
handleOnClick(){
doOtherStuff();
document.getElementById('container').style.backgroundImage = "url('./assets/container2.jpg')"}
return(
<div id="container">
<div onClick={()=> handleOnClick()}
</div>
)
Problems are:
It doesn't work, it changes background to blank screen,
After I leave the component and go back to it, it reverts to the old background. Is there any way to avoid that without having the background linked with state? I already have a lot of things in store and it will start to get messy real soon if I start adding more styles to it.
This is doable by giving your child the ability to change the state in your parent and holding that logic there. Also, it's much better to control your background shift by using this.setState. Just make it a boolean that controls which CSS id to use.
class Parent extends Component {
constructor() {
super()
this.state = {
defaultBackground: true
}
}
toggleChildBackground() {
const newBackground = !this.state.defaultBackground
this.setState({defaultBackground: newBackground})
}
render() {
return (
<div>
<Child
defaultBackground={this.state.defaultBackground}
toggleChildBackground={this.toggleChildBackground.bind(this)}
/>
</div>
)
}
}
class Child extends Component {
handleClick() {
this.props.toggleChildBackground()
}
render() {
return (
<div id={this.props.defaultBackground ? 'id1' : 'id2'}>
<button onClick={this.handleClick.bind(this)}>
change background
</button>
</div>
)
}
}

i am not able retrieve array elements one at a time if i call them in my component all the elements are retrieved at a time

I want to load my array element when an event is occurred by referencing the key i tried different variables for the key but it would not accept all the elements of the array are being displayed if i give index as the key.
I am new to Reactjs and not very familiar with all the syntax and concept can somebody help me with the logic to solve this.
The event I am triggering is onClick or onChange.
`var Qstn =['A','B','C','D','E','F','G','H','I','J'];
<div>
{Qstn.map(function(Q,index){
return <span className="col-md-4 txt" key={index}>{Q}</span>
})}
</div>`
Ok I made a codepen with an example
http://codepen.io/lucaskatayama/pen/QGGwKR
It's using ES6 classes components, but it's easy to translate.
You need to set initial state to an empty array like [].
On click button, it call onClick() method which uses this.setState({}) to change component state.
When React notice state changes, it re-render the component.
class Hello extends React.Component {
constructor(){
super();
//Initial State
this.state = {
Qstn : []
}
}
onClick(){
//Called on click button
// Set state to whatever you want
this.setState({Qstn : ['A','B','C','D','E','F','G','H','I','J']})
}
render(){
let Qstn = this.state.Qstn; // load state and render
return (
<div>
<button onClick={() => this.onClick()}>Click</button>
<div>
{Qstn.map(function(Q,index){
return <span className="col-md-4 txt" key={index}>{Q}</span>
})}
</div>
</div>
)
}
}
ReactDOM.render(<Hello />, document.getElementById('container'))

Resources