i am newbie in React and recently kick start learning and playing around some third-party components. One trying is (http://gfazioli.github.io/react-switch-button/#demo)
Thus, very soon encounter a silly problem (may be just i can't figure it out how). On below sample, i just can't update the SwitchButton to false state to true. Suppose i try using the button click to trigger the update on SwitchButton but i can't just make it work.
The 'checked' property is String type, tried ('true'|'false'|'checked'|'') or even getting bk to use Boolean but still NO luck, also can NOT see any other property that it could trigger an update on SwitchButton
Grateful if anyone could help a newbie. Many Thanks
import React from 'react'
import SwitchButton from 'react-switch-button'
import 'react-switch-button/dist/react-switch-button.css'
export default class Demo extends React.Component {
constructor() {
super();
this.state = {
testChecked: 'false'
};
this.onButtonClick = this.onButtonClick.bind(this);
}
componentDidMount() {
console.log(this.state.testChecked)
}
onButtonClick(){
this.setState({
testChecked: 'true'
});
console.log(this.state.testChecked);
}
render() {
return(
<div>
react-switch-button
<br/><br/>
<input type='button' value='click' onClick={this.onButtonClick} />
<br/>
{this.state.testChecked}:
<SwitchButton name='switch-1' defaultChecked={false} checked={this.state.testChecked} />
</div>
);
}
}
Looks like the property checked is not being used as far as I can see it in the source here https://github.com/gfazioli/react-switch-button/blob/master/src/react-switch-button.js
With that in mind you want to use the defaultChecked props for switching the state e.g.
<SwitchButton name='switch-1' defaultChecked={this.state.testChecked} />
It's from type React.PropTypes.bool and not React.PropTypes.string you have change your state testChecked to that.
Related
In my code, was trying to render a <DatePicker> in <Field> component of Redux Form, via a function called renderDatePicker(). This function is linked to handleClick() function where the state variable isOpen is set to true.
So ideally, onClick should render the <DatePicker> as it is set to visible. But code doesn't update anything. Where am I doing wrong here?
Note: Rendering <DatePicker> alone directly without the help of <Field component=...>, works fine.
For debugging, complete code is in CodeSandbox,
SimpleForm.js
import React from "react";
import { reduxForm } from "redux-form";
import { Field } from "redux-form";
import DatePicker from "react-mobile-datepicker";
class SimpleForm extends React.Component {
constructor(props) {
super(props);
this.state = {
time: new Date(),
isOpen: false
};
this.handleClick = this.handleClick.bind(this);
}
renderDatePicker = () => (
<DatePicker
value={this.state.time}
isOpen={this.state.isOpen}
onSelect={this.handleSelect}
onCancel={this.handleCancel}
/>
);
handleClick() {
this.setState({ isOpen: true }, function() {
console.log(this.state.isOpen);
});
}
handleCancel = () => {
this.setState({ isOpen: false });
};
handleSelect = time => {
this.setState({ time, isOpen: false });
};
render() {
return (
<div>
<button className="select-btn" onClick={this.handleClick}>
select time
</button>
<Field
name="date"
type="date"
component={this.renderDatePicker}
label={"date"}
/>
</div>
);
}
}
export default reduxForm({
form: "simple" // a unique identifier for this form
})(SimpleForm);
The reason for this behavior lies in the implementation of react-form's Field component. It does a shallow compare of all its properties to decide whether it should rerender. You can change your component's state as much as you like, the reference to this.renderDatePicker won't change.
Field passes properties including an onChange handler and the current value into the field's component stateless function call to notify of changes, but this doesn't really apply here because your toggle button is outside of the field.
So one option that comes to my mind is to move your button into the rendered field and then call onChange(!value).
The easier yet dirtier option would be to use an arrow function in your component property: component={() => this.renderDatePicker()} - this instance changes with every re-render of your SimpleForm (i.e. if the state changes), so it comes with a cost, but depending on the complexity of your application the cost is negligible. To mitigate the impact, you could implement shouldComponentUpdate (just like redux-form's Field does) to decide whether it should rerender or not, based on the current and next isOpen state.
Check this bit in redux-form for more details: https://github.com/erikras/redux-form/blob/master/src/createField.js#L44
How to add a button in infowindow with google-maps-react?
Hello, I'm writing a React app, I was having an issue with changing state inside the InfoWindow from google-maps-react, the solution above helped me get through that hurdle.
Right now however, I'm having an issue with wanting to edit the content inside my InfoWindowEx component. Using the method above I am able to change the state of a text box inside the InfoWindowEx, however, when I click on the text box and I type it will let me type 1 letter and then I will have to click the text box again if I want to type the next letter, etc. I think this issue has to do with state.
I don't know if there is a solution to this, I have been trying a lot of different things, but hopefully someone can help me know what is going on.
Here is my InfoWindowEx component:
<InfoWindowEx
key={currentInfoWindow.id}
id={currentInfoWindow.id}
marker={this.state.activeMarker}
visible={this.state.showingInfoWindow}
selectedPlace={this.state.selectedPlace}
onInfoWindowClose={this.onInfoWindowClose}
>
<div >
{infoWindowEditBoxes}
{infoWindowContent}
</div>
</InfoWindowEx>
the Edit boxes are rendering in conditional statements here are they:
if (this.state.editButton) {
infoWindowEditBoxes = (
<div>
<input key={this.props.marker} id="editedName" type="text" placeholder="New Bathroom Name" onChange={this.handleTextBoxState}></input>
<input key={this.props.marker} id="editedLocationName" type="text" placeholder="New Bathroom Location" onChange={this.handleTextBoxState}></input>
<button onClick={() => this.handleSubmitChangesButtonState()}>Submit Changes</button>
</div>
);
}
else {
infoWindowEditBoxes = null
}
and here is my state change function:
handleTextBoxState = (evt) => {
const stateToChange = {}
stateToChange[evt.target.id] = evt.target.value
this.setState(stateToChange)
console.log(stateToChange)
}
Thanks in advance!
I believe component state is getting updated properly in your example, apparently this behavior is related with InfoWindowEx component itself. The way how it is implemented, setState() causes to a re-render InfoWindow component which leads to losing input focus.
You could consider the following updated version of component which prevents re-rendering of info window if it has been already opened:
export default class InfoWindowEx extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: false
};
this.infoWindowRef = React.createRef();
this.containerElement = document.createElement(`div`);
}
componentDidUpdate(prevProps) {
if (this.props.children !== prevProps.children) {
ReactDOM.render(
React.Children.only(this.props.children),
this.containerElement
);
this.infoWindowRef.current.infowindow.setContent(this.containerElement);
this.setState({
isOpen: true
});
}
}
shouldComponentUpdate(nextProps, nextState) {
if (this.state.isOpen) {
return this.props.marker.position.toString() !== nextProps.marker.position.toString();
}
return true;
}
infoWindowClose(){
this.setState({
isOpen: false
});
}
render() {
return <InfoWindow onClose={this.infoWindowClose.bind(this)} ref={this.infoWindowRef} {...this.props} />;
}
}
Demo
Search function is working perfectly fine in the console log but when I try to assign that value to rows which is a state. So I setState the rows inside the setState in searchHandler. I know I'm making a mistake but I don't know how to rectify it. OMIT THE UNDECLARED STATES, MINIFIED THE CODE TO WHAT'S NEEDED
function searchingFor(searchingTerm) {
return function(x){
// console.log("searching",x);
return x.name.toLowerCase().includes(searchingTerm.toLowerCase())|| false;
}
}
class Main extends React.Component{
componentWillMount(){
this.props.fetchTopicsTableContent(this.state.sortBy,'ASC',0,this.props.match.params.CategoryName).then(result=> (this.setState({rows:result.payload.data})))
this.props.countTableContent(this.props.match.params.CategoryName).then(result=>(this.setState({count:result.payload})));
}
constructor(props){
super(props);
this.state={
rows:"",
searchTerm:"",
items:""
}
}
onSubmit(values){
values.preventDefault();
}
onSearchHandler(e){
this.setState({searchTerm:e.target.value},()=>{
{this.state.rows.filter(searchingFor(this.state.searchTerm)).map(item=>{
console.log(item);
//this.setState({rows:item})
})}
})
}
render(){
return(
<div>
<h3>Topics</h3>
<hr/>
<div>
<form onSubmit={this.onSubmit.bind(this)}>
<input type="text"
className="searchBar"
value={this.state.searchTerm}
onChange={this.onSearchHandler.bind(this)}
/>
</form>
</div>
</div>
)
}
Okay so lets start with binding your functions in the constructor, not in the markup, clean things up :P
Next, i'm not sure you understand how setting state works as your function goes against it's basic use. You are correctly setting the first state and using the callback (Because it takes time for state to actually be set) which is great. The callback function it's where it goes downhill.
Your mapping function is loading up several setState calls instantly, for each one console.log() will run successfully, but only one of the setStates will actually take effect. On top of that, even if it did work, your rows state will only have a single item. Lets try this:
onSearchHandler(e){
this.setState(prevState => {
return {
rows: prevState.rows.filter(searchingFor(e.target.value)),
searchTerm: e.target.value,
}
});
}
That will get you what I assume is the desired result... you should only ever do one setState at a time, unless you are waiting for the callback on each one, because you can't be sure each one will complete before the next.
Your logic is fine, but the code looks clumsy.I refactored the code so that only necessary logic is present and instead of bind use arrow functions.
Here, try this code on codeSandbox
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
constructor(props){
super(props);
this.state = {
rows: ["asd", "bsd", "csd", "dsd", "esd"],
items: []
}
}
onSearchHandler = (e) => {
this.setState({ items: this.state.rows.filter(str => str.toLowerCase().includes(e.target.value.toLowerCase()))})
}
render(){
return (
<div>
<h3>Topics</h3>
<input type="text"
className="searchBar"
onChange={(e) => this.onSearchHandler(e)}/>
<p>{this.state.items.join('\n')}</p>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
Intro
I'm just beggining with react, but I've got a project and I want to be able to affect parent state from a sub components (or however it's made).
The final result is to get a Contact list that can be edited on the fly.
Problem:
The easiest way to simplify the probably, that I have is probably by starting with the TodoApp (from React's site) that i've modified slightly. Instead of having a list item that is staticly constructed from the state
ParentState ---> Content
I want to be able to have something like this
ParentState <--> ContentInput
State of my problem:
The following code is where i'm stuck at. There is a comment down bellow. I would like to have that imput affect the TodoApp's State. Maybe I got it the wrong way, if so, what is the Right Way?
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = { items: [] };
this.handleSubmit = this.handleSubmit.bind(this);
this.showState = this.showState.bind(this);
}
render() {
return (
<div>
<h3>TODO</h3>
<button onClick={this.showState}>Console log current state</button>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input ref="field" />
<button>
Add #{this.state.items.length + 1}
</button>
</form>
</div>
);
}
handleSubmit(e) {
e.preventDefault();
if (!this.refs.field.value.length) {
return;
}
const newItem = {
text: this.refs.field.value,
id: Date.now()
};
this.setState(prevState => ({
items: prevState.items.concat(newItem)
}));
}
showState() {
console.log(this.state)
}
}
class TodoList extends React.Component {
render() {
return (
<ul>
{this.props.items.map(item => (
// MAKE THAT INPUT CHANGE THE PARENT STATE
<li key={item.id}><input type="text" defaultValue={item.text} /></li>
))}
</ul>
);
}
}
ReactDOM.render(<TodoApp />, document.getElementById('root'))
https://codepen.io/smdimagerie/pen/Zvdoaj?editors=0010
If you really need direct communication between your parent and something deep in its render tree, you typically have a questionable design going on that should get cut up into single parent-child communication steps, so that you can ask at each step "is it really necessary that this specific child talks to this specific parent?".
That said, the obvious React way to do this is to pass down a function handler so that children can propagate data to a parent, which can then do "whatever is necessary":
class Parent extends Component {
onChange(e) {
...
}
render() {
return <Child onChange={e => this.onChange(e)}/>
}
}
and then make the child call its this.props.onChange(...) when you need it to trigger functionality in the parent. If you need that to happen in the child's children, then you keep passing it down as far as necessary.
Alternatively, if you have a distance to cover, what you probably need instead is for "maybe some component, I don't know which, and I don't care" to do something based on an event getting generated. In this case, you can either use standard JS custom events dispatched on the document, or use a dipatching service like flux (which for small use cases is absurd overkill).
I didn't know how to specify the title.
Here is my problem:
I have a parent component which passes down it's default state as properties to some child component.
One of these default states is being passed as following:
<FriendButton is_friend={this.props.is_friend} />
FriendButton is in a child component of the parent, thats why it is this.props here
So this is what I am trying to pass to <FriendButton /> :
this.props.is_friend == true
So now inside my <FriendButton /> component I want to deal with that value. Weirdly enough it gets logged as "false".
Here is my simple code to check that:
import React from 'react';
class FriendButton extends React.Component {
constructor(){
super();
this.state = {
friend_status: ""
}
}
componentWillMount(e){
console.log("Friend status at friend button: ",this.props);
if(this.props.is_friend){
this.setState({
friend_status: "Friend"
});
}else{
this.setState({
friend_status: "Friend request"
});
}
}
render() {
return(
<button className="friend-request-button">{this.state.friend_status == "Friend" ? <i className="fa fa-check"></i> : <i className="fa fa-user-plus"></i>}{this.state.friend_status}</button>
);
}
}
export default FriendButton;
Now as you can see I am logging the props in the componentWillMount() and the logg that I receive is : Friend status at friend button: Object {is_friend: false}
When I inspect my component in GoogleChrome React Tools I can clearly see that this property is set to true as you can see in the following image.
This has never happened to me before - any ideas what I might be missing here?
Due to componentWillMount() being fired too early it wasn't able to receive the correct property value and therefore update the state correctly.
Using the componentWillReceiveProps(props){} lifecycle allowed me to achieve what I wanted.
Here is an example code on how I used that function:
componentWillReceiveProps(props){
console.log("Props: ", props);
if(props){
var friend_status = "Friend";
}else{
var friend_status = "Friend request";
}
this.setState({
is_friend: props,
friend_status: friend_status
});
}
This worked for me.