I tried to do a checkbox that changes the text when checkbox is checked or unchecked however it seems that the state was not changed even after the checkbox is triggered. I am trying to follow a tutorial and encounter this error which for some reason can't find what is the problem. Below is my current code.
var UseOfState = class extends React.Component {
constructor(props) {
super(props);
this.state = {isCheck: true};
this.toggleCheckbox = this.toggleCheckbox.bind(this);
}
toggleCheckbox () {
console.log('checkbox triggered');
this.setState = ({isCheck: !this.state.isCheck});
}
render () {
console.log('render');
var msg;
if(this.state.isCheck) {
msg = 'checked';
console.log(this.state.isCheck);
} else {
msg = 'unchecked';
console.log(this.state.isCheck);
}
return (
<div>
<input type="checkbox" onChange={this.toggleCheckbox} defaultChecked={this.state.isCheck}/>
<h3>Check box is {msg}</h3>
</div>
);
}
}
ReactDOM.render(<UseOfState/>,document.getElementById('root'));
Here is my codepen: https://codepen.io/anon/pen/OazZpa
I want to know where is my error from there :)
There is typo on below line
this.setState = ({isCheck: !this.state.isCheck});
to
this.setState({isCheck: !this.state.isCheck});
Then it works fine.
Related
I have a string , i.e,
let string= "Hello <b>Click here</b>";
render() {
return (<div dangerouslySetInnerHTML={this.createMarkup(value)}/>
}
createMarkup = value => {
return { __html: value };
};
What I would like is to be able to add an onclick event to the <b> tag perform state manipulations on click.
The underlying problem is where I had a function which was supposed to render whatever is passed by the API. The API would send a string 'Money received for order ID 123', or could be any string that I have no control over. Later, I got a requirement where the item that is bolded must be clickable, so as to perform some actions. I didn't have any other way to solve it.
How can I achieve this?
Caveat: This sounds like an X/Y problem, where the underlying problem (whatever it is) should be solved differently, so that you don't have to add a click handler to a DOM element created via dangerouslySetInnerHTML (ideally, so you don't have to create DOM elements via dangerouslySetInnerHTML at all). But answering the question you asked: (You've clarified the use case; solution #1 below applies and isn't poor practice.)
I don't think you can do that directly. Two solutions I can think of:
Use delegated event handler on the div: Add a click handler on the div, but then only take action if the click passed through the b element.
Use a ref on the div, and then hook the click handler up in componentDidMount and componentDidUpdate (finding the b element within the div via querySelector or similar), something along these lines:
Here's an example of #1:
<div onClick={this.clickHandler} dangerouslySetInnerHTML={this.createMarkup(string)}/>
...where clickHandler is
clickHandler(e) {
// `target` is the element the click was on (the div we hooked or an element
// with in it), `currentTarget` is the div we hooked the event on
const el = e.target.closest("B");
if (el && e.currentTarget.contains(el)) {
// ...do your state change...
}
}
...or if you need to support older browsers without ParentNode#closest:
clickHandler(e) {
// `target` is the element the click was on (the div we hooked or an element
// with in it), `currentTarget` is the div we hooked the event on
let el = e.target;
while (el && el !== e.currentTarget && el.tagName !== "B") {
el = el.parentNode;
}
if (el && el.tagName === "B") {
// ...do your state change...
}
}
...and where you bind clickHandler in the constructor (rather than using a property with an arrow function; why: 1, 2):
this.clickHandler = this.clickHandler.bind(this);
Live Example:
let string = "Hello <b>Click here</b>";
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
clicks: 0
};
this.clickHandler = this.clickHandler.bind(this);
}
clickHandler(e) {
// `target` is the element the click was on (the div we hooked or an element
// with in it), `currentTarget` is the div we hooked the event on
// Version supporting older browsers:
let el = e.target;
while (el && el !== e.currentTarget && el.tagName !== "B") {
el = el.parentNode;
}
if (el && el.tagName === "B") {
this.setState(({clicks}) => ({clicks: clicks + 1}));
}
// Alternative for modern browsers:
/*
const el = e.target.closest("B");
if (el && e.currentTarget.contains(el)) {
this.setState(({clicks}) => ({clicks: clicks + 1}));
}
*/
}
createMarkup = value => {
return { __html: value };
};
render() {
const {clicks} = this.state;
return [
<div>Clicks: {clicks}</div>,
<div onClick={this.clickHandler} dangerouslySetInnerHTML={this.createMarkup(string)}/>
];
}
}
ReactDOM.render(
<Example />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Here's an example of #2, but don't do this if A) You can solve the underlying problem separately, or B) #1 works:
let string = "Hello <b>Click here</b>";
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
clicks: 0
};
this.divRef = React.createRef();
this.hooked = null;
this.clickHandler = this.clickHandler.bind(this);
}
clickHandler() {
this.setState(({clicks}) => ({clicks: clicks + 1}));
}
hookDivContents() {
// Get the b element
const b = this.divRef.current && this.divRef.current.querySelector("b");
// No-op if it's not there or it's the same element we have hooked
if (!b || b === this.hooked) {
return;
}
// Unhook the old, hook the new
if (this.hooked) {
this.hooked.removeEventListener("click", this.clickHandler);
}
this.hooked = this.divRef.current;
this.hooked.addEventListener("click", this.clickHandler);
}
componentDidMount() {
this.hookDivContents();
}
componentDidUpdate() {
this.hookDivContents();
}
createMarkup = value => {
return { __html: value };
};
render() {
const {clicks} = this.state;
return [
<div>Clicks: {clicks}</div>,
<div ref={this.divRef} dangerouslySetInnerHTML={this.createMarkup(string)}/>
];
}
}
ReactDOM.render(
<Example />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Refs are an "escape hatch" giving you direct DOM access. Don't use refs lightly; usually, there's a better choice.
But again: I would solve the underlying problem, whatever it is, differently.
react-html-parser can convert HTML strings into React components.
using transform callback function you can update any tag in HTML string with JSX tag adding any properties and event listeners.
This is how I used it:
ReactHtmlParser(item.value, {
transform: (node) => {
if (node.name === 'a' && node.attribs && node.attribs.href) {
const matched = node.attribs.href.match(/^activity\/([0-9]+)$/i);
if (matched && matched[1]) { // activity id
return <a
href={node.attribs.href}
onClick={(e) => {
e.preventDefault();
this.props.openActivityModal(matched[1]);
}}
>{node.children[0].data}</a>
}
}
}
})
Here is a clean way to achieve your needs. By splitting your string depending on the <br> tag you can end up with an mappable array of text :
class BoldText extends React.Component {
constructor(props) {
super(props)
this.state = {
input: "Money received for order ID <b>123</b>, wow for real, so <b>cool</b> its insane"
}
}
boldClick = ev => {
console.log('clicked !')
}
render() {
const { input } = this.state
const a = input.split('</b>')
const filter = /<b>.*<\/b>/
const text = input.split(filter)
const clickable = filter.exec(input)
//<b onClick={this.boldClick}></b>
return (
<div>
<p>{a.map(t => {
const [text, bold] = t.split('<b>')
return <span>{text}<b onClick={this.boldClick}>{bold}</b></span>
})}
</p>
</div>
)
}
}
ReactDOM.render(<BoldText />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.production.min.js"></script>
<idv id='root'>
This solution should solve the problem you mentioned in the comments of the answer above. You can put your API call in the componentDidMount lifecycle function and change your state from there.
You can make the parent tag a <form> and set the onClick="SomeFunction()".
From the child tag that has the HTML string, set type="button".
let string= "Hello <b type='button'>Click here</b>";
render() {
return (
<form onClick={SomeFunction} dangerouslySetInnerHTML =
{this.createMarkup(value)}/>
}
createMarkup = value => {
return { __html: value };
};
I have a switch in one of the many tabs such as Help, About etc. as shown below:
As you can see, the value of it is set programmatically based on a state variable in componentDidMount(). But the toggle button doesn't reflect the value accordingly. I want it to always change accordingly when the component is mounted.
I solved the problem. Previously I was doing:
componentDidMount() {
const {solar} = this.props;
document.addEventListener("keydown", this.handleGlobalKeyDown );
var toggleButton = document.getElementsByName("toggle")[0];
toggleButton.value = solar;
}
I changed it to:
componentDidMount() {
const {solar} = this.props;
document.addEventListener("keydown", this.handleGlobalKeyDown );
this.state.checked = solar;
}
And it worked!
I have a string , i.e,
let string= "Hello <b>Click here</b>";
render() {
return (<div dangerouslySetInnerHTML={this.createMarkup(value)}/>
}
createMarkup = value => {
return { __html: value };
};
What I would like is to be able to add an onclick event to the <b> tag perform state manipulations on click.
The underlying problem is where I had a function which was supposed to render whatever is passed by the API. The API would send a string 'Money received for order ID 123', or could be any string that I have no control over. Later, I got a requirement where the item that is bolded must be clickable, so as to perform some actions. I didn't have any other way to solve it.
How can I achieve this?
Caveat: This sounds like an X/Y problem, where the underlying problem (whatever it is) should be solved differently, so that you don't have to add a click handler to a DOM element created via dangerouslySetInnerHTML (ideally, so you don't have to create DOM elements via dangerouslySetInnerHTML at all). But answering the question you asked: (You've clarified the use case; solution #1 below applies and isn't poor practice.)
I don't think you can do that directly. Two solutions I can think of:
Use delegated event handler on the div: Add a click handler on the div, but then only take action if the click passed through the b element.
Use a ref on the div, and then hook the click handler up in componentDidMount and componentDidUpdate (finding the b element within the div via querySelector or similar), something along these lines:
Here's an example of #1:
<div onClick={this.clickHandler} dangerouslySetInnerHTML={this.createMarkup(string)}/>
...where clickHandler is
clickHandler(e) {
// `target` is the element the click was on (the div we hooked or an element
// with in it), `currentTarget` is the div we hooked the event on
const el = e.target.closest("B");
if (el && e.currentTarget.contains(el)) {
// ...do your state change...
}
}
...or if you need to support older browsers without ParentNode#closest:
clickHandler(e) {
// `target` is the element the click was on (the div we hooked or an element
// with in it), `currentTarget` is the div we hooked the event on
let el = e.target;
while (el && el !== e.currentTarget && el.tagName !== "B") {
el = el.parentNode;
}
if (el && el.tagName === "B") {
// ...do your state change...
}
}
...and where you bind clickHandler in the constructor (rather than using a property with an arrow function; why: 1, 2):
this.clickHandler = this.clickHandler.bind(this);
Live Example:
let string = "Hello <b>Click here</b>";
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
clicks: 0
};
this.clickHandler = this.clickHandler.bind(this);
}
clickHandler(e) {
// `target` is the element the click was on (the div we hooked or an element
// with in it), `currentTarget` is the div we hooked the event on
// Version supporting older browsers:
let el = e.target;
while (el && el !== e.currentTarget && el.tagName !== "B") {
el = el.parentNode;
}
if (el && el.tagName === "B") {
this.setState(({clicks}) => ({clicks: clicks + 1}));
}
// Alternative for modern browsers:
/*
const el = e.target.closest("B");
if (el && e.currentTarget.contains(el)) {
this.setState(({clicks}) => ({clicks: clicks + 1}));
}
*/
}
createMarkup = value => {
return { __html: value };
};
render() {
const {clicks} = this.state;
return [
<div>Clicks: {clicks}</div>,
<div onClick={this.clickHandler} dangerouslySetInnerHTML={this.createMarkup(string)}/>
];
}
}
ReactDOM.render(
<Example />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Here's an example of #2, but don't do this if A) You can solve the underlying problem separately, or B) #1 works:
let string = "Hello <b>Click here</b>";
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
clicks: 0
};
this.divRef = React.createRef();
this.hooked = null;
this.clickHandler = this.clickHandler.bind(this);
}
clickHandler() {
this.setState(({clicks}) => ({clicks: clicks + 1}));
}
hookDivContents() {
// Get the b element
const b = this.divRef.current && this.divRef.current.querySelector("b");
// No-op if it's not there or it's the same element we have hooked
if (!b || b === this.hooked) {
return;
}
// Unhook the old, hook the new
if (this.hooked) {
this.hooked.removeEventListener("click", this.clickHandler);
}
this.hooked = this.divRef.current;
this.hooked.addEventListener("click", this.clickHandler);
}
componentDidMount() {
this.hookDivContents();
}
componentDidUpdate() {
this.hookDivContents();
}
createMarkup = value => {
return { __html: value };
};
render() {
const {clicks} = this.state;
return [
<div>Clicks: {clicks}</div>,
<div ref={this.divRef} dangerouslySetInnerHTML={this.createMarkup(string)}/>
];
}
}
ReactDOM.render(
<Example />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Refs are an "escape hatch" giving you direct DOM access. Don't use refs lightly; usually, there's a better choice.
But again: I would solve the underlying problem, whatever it is, differently.
react-html-parser can convert HTML strings into React components.
using transform callback function you can update any tag in HTML string with JSX tag adding any properties and event listeners.
This is how I used it:
ReactHtmlParser(item.value, {
transform: (node) => {
if (node.name === 'a' && node.attribs && node.attribs.href) {
const matched = node.attribs.href.match(/^activity\/([0-9]+)$/i);
if (matched && matched[1]) { // activity id
return <a
href={node.attribs.href}
onClick={(e) => {
e.preventDefault();
this.props.openActivityModal(matched[1]);
}}
>{node.children[0].data}</a>
}
}
}
})
Here is a clean way to achieve your needs. By splitting your string depending on the <br> tag you can end up with an mappable array of text :
class BoldText extends React.Component {
constructor(props) {
super(props)
this.state = {
input: "Money received for order ID <b>123</b>, wow for real, so <b>cool</b> its insane"
}
}
boldClick = ev => {
console.log('clicked !')
}
render() {
const { input } = this.state
const a = input.split('</b>')
const filter = /<b>.*<\/b>/
const text = input.split(filter)
const clickable = filter.exec(input)
//<b onClick={this.boldClick}></b>
return (
<div>
<p>{a.map(t => {
const [text, bold] = t.split('<b>')
return <span>{text}<b onClick={this.boldClick}>{bold}</b></span>
})}
</p>
</div>
)
}
}
ReactDOM.render(<BoldText />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.production.min.js"></script>
<idv id='root'>
This solution should solve the problem you mentioned in the comments of the answer above. You can put your API call in the componentDidMount lifecycle function and change your state from there.
You can make the parent tag a <form> and set the onClick="SomeFunction()".
From the child tag that has the HTML string, set type="button".
let string= "Hello <b type='button'>Click here</b>";
render() {
return (
<form onClick={SomeFunction} dangerouslySetInnerHTML =
{this.createMarkup(value)}/>
}
createMarkup = value => {
return { __html: value };
};
I got a textbox that once the user stops typing, I want to update the results(what ever they type will be applied to an array of data and will filter down anything that does match what they type).
Right now I am using onBlur but this of course will only activate after they leave the textbox.
Would it be better to do onChange and put a timer that gets cancelled if they continue to type?
Or is there a better way.
Not sure if it makes a difference but I am reactjs
Vanilla javascript (no React)
var inputElm = document.querySelector('input');
inputElm.addEventListener('input', onInput);
function onInput(){
var duration = 1000;
clearTimeout(inputElm._timer);
inputElm._timer = setTimeout(()=>{
update(this.value);
}, duration);
}
function update(){
console.log('Do something')
}
<input>
In React you would probably do it like this:
class SomeComp extends Component {
constructor(props){
super(props);
this.state = {
inputValue: ''
}
}
onInput = (e) => {
var duration = 1000;
clearTimeout(this.inputTimer);
this.inputTimer = setTimeout(()=>{
this.updateInputValue( e.target.value );
}, duration);
}
updateInputValue = ( value )=> {
this.setState({
inputValue: value
});
}
render(){
return(
<input value={this.state.inputValue} onChange={this.onInput(evt)}/>
)
}
}
Just use onkeyup event it will fire when user releases keyboard button.
document.getElementById('inputText').onkeyup = UpdateData()
I could explain what I am trying to do, but this ReactJS example is a walkthrough of exactly what I want. The problem is I can't figure out what the equivelant would be for react native.
Basically, when I press return in the TextInput, I want the text cleared and focus maintained.
Any thoughts?
I've submitted a PR with a blurOnSubmit property.
Set it to false and the TextInput never blurs, onSubmitEditing still fires though.
Hopefully it gets merged. :)
https://github.com/facebook/react-native/pull/2149
I came out with following (working) solution:
var NameInput = React.createClass({
getInitialState() {
return {
textValue: ''
}
},
clearAndRetainFocus: function(evt, elem) {
this.setState({textValue: elem.text});
setTimeout(function() {
this.setState({textValue: this.getInitialState().textValue});
this.refs.Name.focus();
}.bind(this), 0);
},
render() {
return(
<TextInput
ref='Name'
value={this.state.textValue}
onEndEditing={this.clearAndRetainFocus} />
)
}
});
So, basically when we end editing, we will set the textValue state to the value of the TextInput and right after that (in setTimeout), we switch it back to default (empty) and retain focus on the element.
I don't know how to trigger blurOnSubmit but if you do and it works you should do that. Another thing I found that works with a functional react component in a chat application i am making is this:
... import statments
const ChatInput = props => {
const textIn = React.useRef(null) //declare ref
useEffect(()=>textIn.current.focus()) //use effect to focus after it is updated
const textInputChanged = (text) =>{
props.contentChanged(text);
}
const submitChat = () =>{
const txt = props.content.trim()
txt.length >0 ? props.sendChat(txt, props.username) : null;
}
const keyPressEvent = (e) =>{
return e.key == 'Enter'? submitChat() : null;
}
return (
<TextInput
style={styles.textInput}
keyboardType={props.keyboardType}
autoCapitalize={props.autoCapitalize}
autoCorrect={props.autoCorrect}
secureTextEntry={props.secureTextEntry}
value={props.content}
onChangeText={textInputChanged}
onKeyPress={keyPressEvent}
autoFocus={true} //i don't think you need this since we are using useEffect
ref={textIn} //make it so this is the ref
/>
)}
... export default react-redux connect stuff
if you have more inputs you can probably do some sort of ref choosing logic in the useEffect hook
this is the article that helped me figure it out, it's almost the same thing:
https://howtocreateapps.com/how-to-set-focus-on-an-input-element-in-react-using-hooks/