Pass function inside string literal to other component - javascript

I want to pass a function defined in my actions to an element.
Reducer
case 'UPDATE_HEADER':
return Object.assign({}, state, {
headerChildren: state.headerChildren.concat([action.child])
});
Action.js
export const deleteHeader = () => {
return {
type: 'DELETE_HEADER',
};
}
I am passing this action to my sidebar
onAddHeader () {
this.props.addHeader();
this.props.updateHeader(
`<Header key='${this.props.childKey}'
deleteHeader='${this.props.deleteHeader}'/>`,
);
}
The deleteHeader function is being passed to the Header component but the problem is that it is being passed as a string and not as a function.
I am rendering it as this
`{this.props.headerChildren.map((value, index) => (
<JsxParser
key={index}
components={[Header]}
jsx={value}
/>
))}`
Can someone please guide me through the solution. I have searched a lot and couldn't find a way to do it. If this is not the correct approach to do so, kindly let me know the proper way to do it. I am new to React + Redux so finding it a bit difficult.
Thanks in advance

I am going to take a stab at guessing what I think you want to achieve here - it is a bit unclear so if I am wrong please feel free to comment and I can try and fix.
You don't need to use a string template if you are not trying to pass a string. You can simply pass the component and props themselves. Something like this...
// I'm assuming you're doing something like this
import Header from './wheverYourHeaderIs';
...
onAddHeader () {
this.props.addHeader();
this.props.updateHeader(Header,
{ key: this.props.childKey,
deleteHeader: this.props.deleteHeader
}
);
}
I turned your props into 1 keyed object so it is flexible to add more/less props without having to modify the number of arguments your updateHeader function has to take.
Then your updateHeader would do something like :
function updateHeader(Component, props) {
// do whatever you were going to do with your component
<Component {...props} />
// becasue we combined props into one object
// this is the same as <Header key={this.props.childKey} deleteHeader={this.props.deleteHeader}/>
}
Not sure if this is what you were trying to achieve please feel free to comment if not.

You don't need to use $ or single quotes when passing props these kind of props.. You just need to enclose the value to be evaluated in {}.. It is only when you need to pass a string literal do you use single quotes.
So use something like this instead
<Header key={this.props.childKey} deleteHeader={this.props.deleteHeader}/>

Related

Can't seem to pass return of React custom hook directly to React context

The pattern I often follow when building React apps is to create a custom React hook that returns state data to me, then assign those state values to a React context so I can have access to them in my component hierarchy. The code usually looks like this:
const { notesData, notesDataError } =
useNotes();
const contextValue = { notesData, notesDataError };
return (
<NotesContext.Provider value={contextValue}>
<NoteList />
</NotesContext.Provider>
);
I was thinking I can shorten but just passing what is returned from useNotes() and assign that to the contextValue without having to explicitly name every property
When I try that (see code below), it seems the values don't pass. I expect that the top code will behave the same as the bottom code, but instead, my value for notesData is undefined which I assume means because the properties did not go through.
const contextValue = useNotes();
return (
<NotesContext.Provider value={contextValue}>
<NoteList />
</NotesContext.Provider>
);
Could someone tell me how to get those values to go through without having to mention every one of them?
I'm not sure why what you're trying to do doesn't work, but here's a workaround that should get you what you want:
(Creating a new object with the spread operator)
const contextValue = useNotes();
return (
<NotesContext.Provider value={{...contextValue}}>
<NoteList />
</NotesContext.Provider>
);
And I'm not sure about the efficiency of this workaround, but it should be the same (if not faster) than manually destructuring each key and making a new object
The example actually does work. I am the poster of the question and I was not initializing the state data correctly in the custom hook.

Where do function parameters come from in javascript?

I am using the following code:
handleOwnerMode = ownerChecked => {
this.setState(prev => ({ ownerChecked, showOwner: !prev.showOwner}))
// this.setState(prev => ({ ownerChecked: !prev.ownerChecked, showOwner: !prev.showOwner }))
}
Inside the render is
<Switch onChange={this.handleOwnerMode} checked={this.state.ownerChecked} />
OnChange, I somehow was able to receive what was changed about ownerChecked. Why is this the case? I didn't write onChange={this.handleOwnerMode(event.value)} or anything like that...
Also, for setState. I normally just use this.setState( { state: newState} ), but now I can somehow pass in the previous state with this.setState( prev => {} ). Is there defined overloading somewhere that lets me do this?
Thank you.
OnChange, I somehow was able to receive what was changed about
ownerChecked. Why is this the case? I didn't write
onChange={this.handleOwnerMode(event.value)} or anything like that...
In both cases you have passed a function (callback) to "receivers". First time to Switch Component, second time to React. Each of them can call your function/callback using any parameter they want. This is how normally callbacks are used.
Is there defined overloading somewhere that lets me do this?
Yeah probably setState checks if you passed a function to it and behaves differently: e.g. gives you previous state and calls your function. If you pass an object to it, it doesn't do that.
React events are synthetic so that even when yo do not pass any event parameters, function takes it. Take a look at these. More information 1, More information 2.

Calling a method vs using a function to call a method

Suppose we have a method inside a class like this
class Blog extends Component {
postClicked = (id) => {
this.setState({selectedPostId: id})
}
render () {
const newPosts = this.state.posts.map(el => {
return <Post key={el.id}
title={el.title}
author={el.author}
onClick={this.postClicked(el.id)}/>
})
return
//something
{post}
}
}
}
Now, What is the difference between calling the handler like this
onClick={this.postClicked(el.id)} and onClick={() => this.postClicked(el.id)}
Would appreciate if someone can tell me the difference in general
after Ecmascript 6 javascript was introduced with is arrow function link
here ()==>{//code} is a similar as a function() or anonymous function
tell me if you find out what you want
The first option, "this.postClicked(el.id)", will actually call the method, "this.postClicked", with the "el.id" argument, each time the component renders (probably not what's intended).
The second option, "() => this.postClicked(el.id)", will only call the method, "this.postClicked", with the "el.id" argument, when "Post" is clicked.
Overall, if you can find a way to put the "el.id" argument into an "id" or "name" prop on the component
<Post id={el.id} />
then you can do:
<Post
id={el.id}
onClick={this.postClicked}
/>
this.postClicked = (event) => {
const { id } = event.target;
...
}
This last option avoids the use of an unnamed function. If you use an unnamed function, it will cause unnecessary re-renders. React cannot tell that an unnamed function is the same when it's checking whether or not it should re-render, by considering if the props of a component have changed. It considers the unnamed functions to be a new prop each time it checks, causing an unnecessary re-render each time.
Overall, it won't break your app, but it slows down performance slightly if you do it enough. It comes up especially if you start using React Motion (you'll really notice a difference there). It's best to avoid unnamed functions if possible.
you can read this blog it wil clear the things https://medium.com/#machnicki/handle-events-in-react-with-arrow-functions-ede88184bbb
Differences are,
First method is a wrong implementation and it wont give the intended result, where as second one will work.
In the first method you are making a function call, in second one you are assigning a function's signature to onClick.
It is like the combination of below two statements.
var variableName = function(){//some content};
onClick={variableName}
It looks like you question has already been answered. Just a side note though: remember that when assigning your method with an arrow function
onClick={ () => this.method() }
a new anonymous function is created on every re-render. So if the method doesn't need any arguments, it's better to reference the method directly (without parentheses so it's not invoked).
onClick={ this.method }
The first will call the function every time render is done.
The second will do what you want - call it onClick.

Render custom React component within HTML string from server

I have a HTML string that comes from the server, for example:
const myString = '<p>Here goes the text [[dropdown]] and it continues</p>`;
And I split this string into 3 parts so the result is the following:
const splitString = [
'<p>Here goes the text ',
'[[dropdown]]',
' and it continues</p>'
];
Then I process those 3 parts in order to replace the dropdown with a React component:
const processedArr = splitString.map((item) => {
if (/* condition that checks if it's `[[dropdown]]` */) {
return <Dropdown />;
}
return item;
}
So after all, I get the processed array, which looks like this:
['<p>Here goes the text ', <Dropdown />, ' and it continues</p>']
When I render that, it renders the HTML as a text (obviously) with the Dropdown component (that renders properly) in between the text. The problem here is that I cannot use { __html: ... } because it has to be used such as <div dangerouslySetInnerHTML={{ __html: ... }} />. I cannot add <div> around the string because that would cut out the <p> tag.
I thought about splitting the parts into tags and then in some sort of loop doing something like:
React.createElement(tagName, null, firstTextPart, reactComponent, secondTextPart);
but that would require fairly complex logic because there could be multiple [[dropdown]]s within one <p> tag and there could be nested tags as well.
So I'm stuck right now. Maybe I'm looking at the problem from a very strange angle and this could be accomplished differently in React. I know that React community discourages rendering HTML from strings, but I cannot go around this, I always have to receive the text from the server.
The only stackoverflow question I found relevant was this one, however that supposes that content coming from backend has always the same structure so it cannot be used in my case where content can be anything.
EDIT:
After some more digging, I found this question and answer which seems to be kinda solving my problem. But it still feels a bit odd to use react-dom/server package with its renderToString method to translate my component into a string and then concatenate it. But I'll give it a try and will post more info if it works and fits my needs.
So after playing with the code, I finally came to a "solution". It's not perfect, but I haven't found any other way to accomplish my task.
I don't process the splitString the way I did. The .map will look a bit different:
// Reset before `.map` and also set it up in your component's constructor.
this.dropdownComponents = [];
const processedArr = splitString.map((item) => {
if (/* condition that checks if it's `[[dropdown]]` */) {
const DROPDOWN_SELECTOR = `dropdown-${/* unique id here */}`;
this.dropdownComponents.push({
component: <Dropdown />,
selector: DROPDOWN_SELECTOR
});
return `<span id="${DROPDOWN_SELECTOR}"></span>`;
}
return item;
}).join('');
Then for componentDidMount and componentDidUpdate, call the following method:
_renderDropdowns() {
this.dropdownComponents.forEach((dropdownComponent) => {
const container = document.getElementById(dropdownComponent.selector);
ReactDOM.render(dropdownComponent.component, container);
});
}
It will make sure that what's within the span tag with a particular dropdown id will be replaced by the component. Having above method in componentDidMount and componentDidUpdate makes sure that when you pass any new props, the props will be updated. In my example I don't pass any props, but in real-world example you'd normally pass props.
So after all, I didn't have to use react-dom/server renderToString.
How about break the text apart and render the component separately?
your react component should look like this (in JSX):
<div>
<span>first text</span>
{props.children} // the react component you pass to render
<span>second part of the text</span>
</div>
and you would just call out this component with something like:
<MessageWrapper>
<DropdownComponent/> // or whatever
</MessageWrapper>

ReactJS call class method in Button after map()

I have 2 JS classes: App.jsx, Note.jsx.
Note.jsx:
export default class Note{
constructor(text) {
this.text = text;
}
changeNoteStatus(){
console.log(this.text);
}
}
In App.jsx:
let tmpNote = new Note(String(this.state.value));
this.notes.push({key:this.currId, value:tmpNote});
and in App.jsx i can call the changeNoteStatus() on a simple object but after creating a list of Note and using map() function:
return this.notes.map((obj) =>
<div className="note" onClick={obj.value.changeNoteStatus} key={obj.key} id={"note_" + obj.key}/>
)
The method doesn't recognize class field anymore (like this.text).
I have tried {this.notes[obj.key].changeNoteStatu} in mapping function too.
Please help me.
When you use it in an onClick like that, the handler will get called with the wrong this value.
One solution is to use Function#bind:
onClick={obj.value.changeNoteStatus.bind(obj.value)}
// --------------------------------^^^^^^^^^^^^^^^^
...but depending on how your overall code is structured, there may be more concise or efficient ways of doing it.

Categories

Resources