I am trying to change my state from the render function but it does not work.
render() {
return (
<View>
// some code
{this._renderModal()}
</View>
)
}
_renderModal() {
let channel = this.props.pusher.subscribe('my-channel');
channel.bind('my-event', data => {
this.setState({
requestedToEnter: false
});
this.props.navigation.getNavigator('root').push(Router.getRoute('order', { restaurant_id }));
});
return (
<Modal
animationType={'slide'}
transparent={false}
visible={this.state.requestedToEnter}
onRequestClose={this._handleEnter}>
// some components
</Modal>
)
}
When I receive the my-event on my-channel it fires the function that is bind to it. The setState method does not change the state (or it does but do not re-render the component). I think it's because I am using the setState method in the event function.. How I can close the Modal when I receive an event from pusher?
This is a bad practice to call this.setState function in your render() as each time setState is called, the component will render itself with the updated state values binding to your components / subcomponents.
Alternatively, you can create a state variable
this.state = {
isModalOpen: true
}
And put your pusher event listener in componentDidMount() and update your state variable value in the callback. Then, control your modal component visibility with the state variable:
<Modal
animationType={'slide'}
transparent={false}
visible={this.state.isModalOpen}></Modal>
Related
With jquery I would have a modal that I would reference using the DOM id and then inject the template using jQuery etc.
With Reactjs, would it be better for me to do the following:
onClick will dispatch an action and pass the parameters
my redux reducer will then update the state with those parameters
the modal's CSS will update updated based on the state that just changed, and then display the modal window.
Is that the react way to do it?
The new and improved way to do modals is via Portals
They have a basic modal example on that page. For posterity, I'll include the example here:
// These two containers are siblings in the DOM
const appRoot = document.getElementById('app-root');
const modalRoot = document.getElementById('modal-root');
class Modal extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement('div');
}
componentDidMount() {
// The portal element is inserted in the DOM tree after
// the Modal's children are mounted, meaning that children
// will be mounted on a detached DOM node. If a child
// component requires to be attached to the DOM tree
// immediately when mounted, for example to measure a
// DOM node, or uses 'autoFocus' in a descendant, add
// state to Modal and only render the children when Modal
// is inserted in the DOM tree.
modalRoot.appendChild(this.el);
}
componentWillUnmount() {
modalRoot.removeChild(this.el);
}
render() {
return ReactDOM.createPortal(
this.props.children,
this.el,
);
}
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {clicks: 0};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// This will fire when the button in Child is clicked,
// updating Parent's state, even though button
// is not direct descendant in the DOM.
this.setState(prevState => ({
clicks: prevState.clicks + 1
}));
}
render() {
return (
<div onClick={this.handleClick}>
<p>Number of clicks: {this.state.clicks}</p>
<p>
Open up the browser DevTools
to observe that the button
is not a child of the div
with the onClick handler.
</p>
<Modal>
<Child />
</Modal>
</div>
);
}
}
function Child() {
// The click event on this button will bubble up to parent,
// because there is no 'onClick' attribute defined
return (
<div className="modal">
<button>Click</button>
</div>
);
}
ReactDOM.render(<Parent />, appRoot);
Typically if you're using React you want to stay away from other DOM manipulation libraries and just do all your DOM manipulations in React. The way you have described is how I typically open modals in React. I also use react-modal to make this a little easier.
I need to upload a file to the server for that i have given fileChangedHandler function for onClick event. but in that function setState is skipping because of asychronous operation.
i am giving my code below.
class MultiSelectField extends React.Component {
constructor(props) {
super(props);
this.state = { //image: '',
selectedFile: null};
this.fileChangedHandler = this.fileChangedHandler.bind(this);
this.fileUploadHandler = this.fileUploadHandler.bind(this)
}
fileChangedHandler = (event) => {
this.setState({selectedFile:event.target.files[0]}) // asynch and skipping
console.log(this.state.selectedFile)
this.forceUpdate()
}
fileUploadHandler = () => {
const fd = new FormData()
fd.append('myFile', this.state.selectedFile, this.state.selectedFile.name)
axios.post('my-domain.com/file-upload', formData)
console.log("selected"+JSON.stringify(this.state.selectedFile))
console.log("form data : "+fd)
}
render() {
return (
<div>
<input type="file" onChange={this.fileChangedHandler} ref="file" />
<button onClick={this.fileUploadHandler}>Upload!</button>
</div>
);
}
}
i am not getting the data while consoling this.state.selectedFile it showing null.
Thanks in advance
You can hide Upload Button initially and show it after selected file is set to state in a callback.
fileChangedHandler = (event) => {
this.setState({selectedFile:event.target.files[0], showUploadButton:true}, () =>{
// file has been selected
console.log(this.state.selectedFile) <-- console.log in callback
}) // asynch and skipping
this.forceUpdate()
}
render
{this.state.showUploadButton && <button onClick={this.fileUploadHandler}>Upload!</button>}
Now upload button will appear once the file has been set to state, then you'll get it.
However your problem relies here -
this line should be inside the callback after you set the state as I mentioned above.
console.log(this.state.selectedFile)
Callback of setState method makes sure that state has been set.
update
fileChangedHandler = (event) => {
this.setState({selectedFile:event.target.files[0], showUploadButton:true}, () => {
console.log(this.state.selectedFile)
this.forceUpdate()
})
}
so why this worked?
React setState method is asynchronous which is why you receive confirmation via callback when a state is set in component.
Another question may arise in your mind that why its asynchronous whether it does not call any external resource to set state (API or server call).
The answer in simple words is -
Because on every state change in React component it re-renders render() method or Virtual DOM. Now suppose you have 100s of states to be set in a component on one click, Then this should be rendering virtual DOM 100s of times. Which react actually doesn't do.
It creates batches of states. Say bifurcation of 100s of state into 2-3 batches. Now from UI perspective it accepts multiple states at once but set them in batches by grouping the states into a few batches that keeps UI free and in non blocking state.
That is why the term asynchronous is used to setState.
Using setState callback to call forceUpdate is unneccessary - setting state will force update (new render) ;)
Callback is usable when setting state sholud fire some actions after change is done - in this case following action is trigerred manually. KISS & readable ;)
fileChangedHandler = (event) => {
this.setState({selectedFile:event.target.files[0]})
}
you can disable upload button with simple condition
render() {
return (
<div>
<input type="file" onChange={this.fileChangedHandler} ref="file" />
<button onClick={this.fileUploadHandler} disabled={!this.state.selectedFile}>Upload!</button>
</div>
);
}
or hide it
render() {
return (
<div>
<input type="file" onChange={this.fileChangedHandler} ref="file" />
{!this.state.selectedFile &&
<button onClick={this.fileUploadHandler}>Upload!</button>
}
</div>
);
}
I need to make sure an input element is focused when the following is true:
DOM is available
and properties got changed
Question: Do I need to put my code in both componentDidUpdate and componentDidMount or just componentDidUpdate would be suffice?
private makeSureInputElementFocused() {
if (this.props.shouldGetInputElementFocused && this.inputElement !== null) {
this.inputElement.focus();
}
}
componentDidMount() {
this.makeSureInputElementFocused(); // <-- do i need this?
}
componentDidUpdate() {
this.makeSureInputElementFocused();
}
You have to use both.
componentDidMount()
componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.
componentDidUpdate()
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
You also could place it into the render() method which seems like it's appropriate for your case since you always want to check the focus. Then you don't need to put it into componentDidMount() and componentDidUpdate()
Each of your conditions require you to place the code inside 1 function each:
the DOM is available and - componentDidMount
properties got changed - componentDidUpdate
So you have to place inside both functions.
Another option is to call setState() inside componentDidMount, so that componentDidUpdate is invoked.
componentDidUpdate is not called at initial render (see https://reactjs.org/docs/react-component.html#componentdidupdate) so you probably must call it twice as in your example.
componentDidMount()
componentDidMount() will be invoked immediately after a component is mounted. This method will render only once and all the initialization that requires DOM nodes should go here. Setting state in this method will trigger a re-rendering.
componentDidUpdate()
componentDidUpdate() is invoked immediately every time the updating occurs. This method is not called for the initial render.
You can understands more from this below example
import React from 'react';
class Example extends React.Component{
constructor(props) {
super(props);
this.state = {
count: 0
};
}
componentDidMount() {
//This function will call on initial rendering.
document.title = `You clicked ${this.state.count} times`;
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}
render(){
return(
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
)
}
}
export default Example;
You can understand by commenting and un-commenting both methods.
In React v16.7.0-alpha you can use the useEffect hook:
import React, { useEffect, useRef } from "react";
function InputField() {
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus();
});
return <input ref={inputRef} />;
}
From the docs:
If you’re familiar with React class lifecycle methods, you can think
of useEffect Hook as componentDidMount, componentDidUpdate, and
componentWillUnmount combined.
Example
Note: as mentioned, this will not work with class based components.
I have a modal component that should be called when setState changes but for some reason it is not updating. In the first file Im setting the following in render.
<ModalParcel showModal={this.state.showModal} parcelToConfirm={this.state.dataForParcelToConfirm} />
Within the Modal component constructor() I am setting the following.
constructor(props) {
super(props);
console.log("Constructor for modal componenet called.")
//this.renderRowForMain = this.renderRowForMain.bind(this);
this.state = {
show: this.props.showModal
};
}
render() {
if(this.state.show == true){
var content =
<View style={styles.modal}>
<View style={styles.modalContent}>
<TouchableHighlight onPress={() => this.closeModal()}>
<Text>Close</Text>
</TouchableHighlight>
</View>
</View>;
}else {
var content = null;
}
return ( content );
}
The problem is that the constructor() only gets called once on initial creation. I was under the impression that when I update the state to show the modal the constructor would be called again but this isn't happening.
I basically want to change the state to show the modal and then re-run render().
No constructor will not get called when component re-rendered, it will get called only on initial rendering.
I think, there are two ways by which you can solve your issue:
1. Use componentWillReceiveProps(){ lifecycle method it will get called whenever any change happens to props values, so you can update the Modal component state value showModal here, like this:
componentWillReceiveProps(nextProp){
this.setState({
show: nextProps.showModal
});
}
As per DOC:
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.
2. Don't store the props value in state variable and directly use this.props.showModel in Model component, by this way whenever any change happens to props values, Modal component will take the updated values.
I'm trying to set the state of my PlayerKey component here however the state won't update on a onClick action:
class PlayerKey extends Component {
constructor(props) {
super(props);
this.state = {
activeKeys:[]
}
}
activateKey = (e) => {
this.setState({
activeKeys:["2","3"]
})
}
render() {
return (
<div className="key" data-row-number={this.props.rowKey} data-key-number={this.props.dataKeyNumber} onClick={this.activateKey}></div>
)
}
}
I've tried console logging this.state in activateKey and it gives me the state of the component no problem (the blank array) so not sure why I can't update it?
setState method in react is asynchronous and doesn't reflect the updated state value immediately.
React may batch multiple setState() calls into a single update for performance.
So accessing the recently set state value might return the older value. To see whether the state has really been set or not, You can actually pass a function as callback in setState and see the updated state value. React Docs
As in your case, you can pass a function as callback as follows.
activateKey = (e) => {
this.setState({
activeKeys:["2","3"]
}, () => {
console.log(this.state.activeKeys); // This is guaranteed to return the updated state.
});
}
See this fiddle: JSFiddle