I made todo app using jquery and now using react making same app. I made general layout of the app using react but facing problem in appending element by clicking button. The code i made in jquery was.
function entervalue()
{
var text = $('#inputext').val();
$("#list").append('<li class="item"> <input type="checkbox" class="option-input checkbox"> ' + text + '<button class="destroy"></button><li>');
$("#inputext").val('');
}
Here the value of input field is stored in var text.
List is appended in having checkbox, 'text' and a button.
I want same code to be written in react, so that when i click on button the following list gets appended.
Use state to store the new value. Save it as an array. React does a re-render when the state changes. During that render, map() through the value in the state and generate the HTML out of it.
this.setState({ //put this inside the 'onChange' handler of <input/>
value: e.target.value
})
This will set the new value to the state.
var todo = this.state.value.map(function(value){
return <li class="item"> <input type="checkbox" class="option-input checkbox">{value}<button class="destroy"></button><li>
})
todo will have the latest list of user inputs.
[UPDATE]
Check the example https://jsfiddle.net/Pranesh456/1veb7bdg/1/
jQuery is fundamentally different than React... you need to think of how you store data in state.. not actual DOM elements (jQuery like). You'll want to define your DOM elements in your render method and have it iterate through your state to generate the elements.
I made this repo to help teach a developer the transition from jQuery to React.. it might be helpful to checkout: https://github.com/bradbumbalough/react-tunes.
I also highly reccomend this doc if you're new to React: Thinking in React.
Related
How can I change the className or style of a div without using state or any third party libraries? Lets say I click on a button, and I need to change the background color of a div how can I do that?
<Affix onChange={() => change css or class} offsetTop={60}>
<div>...</div> // Change css of this div
</Affix>
You can change any attribute or property of a Component (Element) in React by using basic javascript functions.
onClick={(e) => {
e.currentTarget.setAttribute("src", newUrl);
}
Will change an image the moment you click on it, without using Ref or State.
event.currentTarget will give you the reference to the component that fired that particular React.MouseEventHandler event, and with the Element's reference, you can manipulate it at will.
This is particularly useful when you need to change an attribute in a component in a map loop without needing to keep track of it.
Edit:
A friend of mine just gave me a better one for classes in specific:
e.currentTarget.classList.add('my_custom_klass')
You can either do it manually using state:
const [myClass, setMyClass] = useState('bgColor-white');
return (
<Affix onChange={() => setMyClass('bgColor-black')} offsetTop={60}>
<div className={myClass}>...</div> // Change css of this div
</Affix>
)
Or you can use a library that handles dynamic styling. I use and recommend styled-components
I'm working on a simple blog/CMS tool. In authoring content, I'm supporting an option to enter raw html/css. The user enters this content into a text area, and I can render this into a page using dangerouslySetInnerHtml. That works fine.
However, I'd really like to embed some React components the content as well. Ideally I'd like to enter something like this into a textarea...
<div>
<p>Some content</p>
<MyPictureComponent url="..." />
</div>
...and then render that into a page and have it create the MyPictureComponent.
I'll be storing the above "code" in a database, and rendering it dynamically as users view the "post". Is it possible to rendering that raw text as functioning React?
I saw this project (HTML to React), which seemed promising, bu that seems to only parse the HTML given to it, not tags for React components.
I found a way to do what I want, with the caveat that it's somewhat manual, and potentially dangerous. However, in my case, I'm creating a blog/CMS for a very limited audience, and the concern about users potentially inserting harmful content is non-existent.
My approach ended up using html-to-react (https://www.npmjs.com/package/html-to-react). Html-to-react accepts a string (containing raw HTML markup), and transforms it into a proper React component. By default, its parse() method doesn't properly handle React components (it just turns them into lower-case-named html elements). However, the library has a parseWithInstructions, which allows you to control how individual nodes in the component are rendered.
In my case, I want to enable certain React components to be rendered. One of those is my ExternalLink component. What follows is the method I use to transform some user-entered raw HTML into a React component that properly rendered my components.
updatePreview() {
// Combine the user-entered CSS and the user-entered HTML into a single string.
let outputPreview = "<div><style>" + this.state.cssValue + "</style><div>" + this.state.inputValue + "</div></div>";
let htmlToReactParser = new HtmlToReact.Parser();
let processNodeDefinitions = new HtmlToReact.ProcessNodeDefinitions(React);
let processingInstructions = [
{
// Custom <ExternalLink> processing
shouldProcessNode: function (node) {
return node.name === 'externallink';
},
processNode: function (node, children) {
let attribs = node.attribs;
return <ExternalLink {...attribs}>{children}</ExternalLink>
}
},
{
// Anything else
shouldProcessNode: function (node) {
return true;
},
processNode: processNodeDefinitions.processDefaultNode
}];
// Convert the HTML into a React component
let reactComponent = htmlToReactParser.parseWithInstructions(outputPreview, () => true,
processingInstructions);
// Now that we have a react component, we set it to the state.
// Our render() method includes a "{this.state.outputPreview}", which causes the
// component to be rendered.
this.setState({outputPreview: reactComponent, refreshPreviewTimer: null});
}
Note that outputString in the first line of the method will contain some raw text like this:
"<div>
<style></style>
<div>
<p>Here's a link:<p>
<ExternalLink url="http://www.google.com">Google</ExternalLink>
</div>
</div>"
There are some approaches I'll take to generalize this approach more, using a dictionary of strings to enable support for a wider range of Components. I'll also look at some approach to automatically importing the desired Component. (Currently, I'm manually importing all supported components.)
So, all credit goes to the author of html-to-react, though I may encourage him to include an example of rendering child components.
I've been working on a React project recently. I've gotten to the point where I know that when typing in a textbox React updates its UI state and re-renders the textbox and brings focus back to it. The whole process happens so fast that user doesn't notice that textbox has been re-rendered while typing.
Now here my question is that why doesn't React just update UI state of textbox at onChange event and doesn't re-render the textbox and hence doesn't require to put focus on it.
Why isn't the re-render process saved in this, or other similar, cases. What were we missing if we dont re-render?
React doesn't re-render elements in the sense that it creates a new one which replaces the old. In your example it simply updates the value.
React will only ever remove an element if it was replaced with one of another type. If it's of the same type, it will simply update the missing or altered attributes. You should have a read about Reacts Reconciliation on the official docs.
Elements Of Different Types
Whenever the root elements have different types, React will tear down the old tree and build the new tree from scratch. Going from <a> to <img>, or from <Article> to <Comment>, or from <Button> to <div> - any of those will lead to a full rebuild.
DOM Elements Of The Same Type
When comparing two React DOM elements of the same type, React looks at the attributes of both, keeps the same underlying DOM node, and only updates the changed attributes.
Demo
I have prepared a demo below. It's a combination of React with jQuery just to demonstrate this.
When you type some text into the textarea, the state is updated and the element value is updated to show what you typed. This is done with React.
The button is outside the React scope completely. There is an eventlistener written with jQuery which randomly alters the elements css background color.
If the component was replaced each time React updated the state value of the component (i.e when we type inside the textarea), the background color would be lost since we would be talking about a newly created DOM element.
However, as you can see, this is not the case -- the background stays.
class Greeting extends React.Component {
constructor() {
super();
this.state = {txt: ""};
}
updateTextarea = (e) => {
this.setState({txt: e.target.value});
}
render() {
return <textarea onChange={this.updateTextarea}>{this.state.txt}</textarea>;
}
}
ReactDOM.render(<Greeting />, document.getElementById('app'));
$("#btn").on("click", function() {
var colors = ["red", "green", "lightblue", "grey", "pink", "orange"];
var rand = Math.floor(Math.random() * 6);
$("#app textarea").css("background", colors[rand]);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
<button id="btn">Randomize color!</button>
My code fetches a blob of HTML and renders it on the page. When the user selects some text within this blob, I want the selected text to be wrapped in its own span. (This is a "highlighting" feature similar Google Docs' comments system.)
If I were doing this with plain Javascript, I'd mutate the DOM on my own. But I'm not sure how to do this safely in React or where in the component lifetime I'd be able to do so.
Ideally, I could directly manipulate the Element corresponding to my HTML blob and use that directly within render(), but I don't know if this would play well with React's bookkeeping.
How can I do this in React without shooting myself in the foot?
EDIT:
Per request, some sample code. Let's say that our component receives these props:
{
id: 1337,
content: "<p>This is a paragraph with some <strong>markup</strong></p>",
highlights: [],
}
And renders something accordingly:
const Widget = React.createClass({
render() {
return <div dangerouslySetInnerHTML={{__html: this.props.content}} />
},
});
Now the component is updated with highlights: [{ start: 5, end: 10 }]. I want to have chars 5 through 10 wrapped in some <span>.
Is the right way to do this to just parse this.props.content as an Element, add the <span> in the right place, and dangerouslySetInnerHTML at the end?
Maybe you can store your <p> element with the text you want to highlight in your state. Like
this.state = {
willHighlight: <p>This is a <span>paragraph</span> with some <strong>markup</strong></p>
}
And then you can conditionaly render it. I think this is a better approach
in my form i have a few dropdown components. Whenever first dropdown option changes i want to update props for the second dropdown and rerender it. My code looks like this
handleProjectChange(option) {
//this.setState({ selectedProject: option })
this.refs.phase.props = option.phases;
//this.refs.forceUpdate()
this.refs.phase.render()
}
render() {
var projectOptions = this.projectOptions
var defaultProjectOption = this.state.selectedProject
var phaseOptions = defaultProjectOption.phaseOptions
var defaultPhaseOption = phaseOptions[0]
var workTypeOptions = api.workTypes().map(x => { return { value: x, label: x } })
var defaultWorkTypeOption = workTypeOptions[0]
return (
<div>
<Dropdown ref='project' options={projectOptions} value={defaultProjectOption} onChange={this.handleProjectChange.bind(this)} />
<Dropdown ref='phase' options={phaseOptions} value={defaultPhaseOption} />
<Dropdown options={workTypeOptions} value={defaultWorkTypeOption} />
<button className="btn btn-primary" onClick={this.handleAddClick.bind(this)}>Add</button>
</div>
)
}
But props are not changed, so it rerenders the same options. At the moment im just rerendering entire form by setting new state on it. Is there any way to rerender only one child/Dropdown with new props?
The way to do this is to put the selected option in first dropdown selectedProject in state.
And inside your render function, fetch/ populate the options in the second dropdown, dependent on the selected project.
Flow will then be:
User selects an option in the first dropdown.
This triggers handleProjectChange()
Inside handleProjectChange(), the newly selected option is put in state, by a this.setState() call
Because state changed, react re-runs the entire render() function.
Under the hood, react figures out that only the second dropdown has changed, so react will only re-render the second drop-down on your screen/ in the DOM.
Although React does have a reconciliation algorithm that dynamically checks whether each component should be rerenader or not in every rendering of its parent, it doesn't always work as we intended.
https://reactjs.org/docs/reconciliation.html
For this kind of issues, you have two options. You can use either React.pureComponent or React.useMemo().