How to rerender inside of .map() list using Gutenberg withSelect - wordpress

I'm creating table of content Gutenberg custom block which reactive reflect h2 text.
Outside of map(), it works. but inside not working.
Please let me know how to modify this code.
thanks.
import { registerBlockType } from "#wordpress/blocks";
import { withSelect, select } from "#wordpress/data";
registerBlockType("theme/toc", {
title: "TOC",
icon: "list-view",
category: "layout",
edit: withSelect((select) => {
return {
blocks: select("core/block-editor").getBlocks(),
};
})(Edit),
save: () => {
return null;
},
});
export function Edit(props) {
const { blocks = [] } = props;
const headings = [];
blocks.forEach((el) => {
if (!(el.name === "core/heading" && el.attributes.level === 2)) {
return;
}
headings.push(el.attributes.content);
});
return (
<div>
<p>{headings[0]}</p> // this line works
<ol>
{headings.map((h2, i) => { // not working
<li key={i}>
<a>{h2}</a>
</li>;
})}
</ol>
</div>
);
}

Simply remove the curly braces so the JSXElemnt can be returned from the map function.
<ol>
{headings.map((h2, i) =>
<li key={i}>
<a>{h2}</a>
</li>;
)}
</ol>
something else to note is it's not advised to use the element index i as the key value. you may want to use a more unique value like id from the element you're looping through.

Related

How to remove unwanted panels inside InspectorControls from core blocks in Gutenberg

If I want to extend the core block I can use Block Filters where I found a filter like editor.BlockEdit.
Example from doc:
const { createHigherOrderComponent } = wp.compose;
const { Fragment } = wp.element;
const { InspectorControls } = wp.blockEditor;
const { PanelBody } = wp.components;
const withInspectorControls = createHigherOrderComponent( ( BlockEdit ) => {
return ( props ) => {
return (
<Fragment>
<BlockEdit { ...props } />
<InspectorControls>
<PanelBody>My custom control</PanelBody>
</InspectorControls>
</Fragment>
);
};
}, 'withInspectorControl' );
wp.hooks.addFilter(
'editor.BlockEdit',
'my-plugin/with-inspector-controls',
withInspectorControls
);
It works, but now i would like to remove unwanted panels from core blocks. Unfortunately, the documentation does not contain any such example so I can't understand how to do this.
So how to remove unwanted panels inside InspectorControls from core blocks in Gutenberg?
Note: I know about the newest feature theme.json, but it has very limited functions and won't remove everything it needs

How to optimise react typography component?

I have code:
const H1 = ({style, children}) => {
return <h1 className={styleMap[style]}>{children}</h1>
}
const H2 = ({style, children}) => {
return <h2 className={styleMap[style]}>{children}</h2>
}
My goal is to reduce repeated code, so that I will have something like:
const H{number} = ({style, children}) => {
return <h{number} className={styleMap[style]}>{children}</h{number}>
}
But I would be able to call it like
<H1 style='style5'>Main Title</H1>
<H2 style='style2'>Subtitle</H2>
const H{number} = ({style, children}) => {
return <h{number} className={styleMap[style]}>{children}</h{number}>
}
This syntax you want is not possible. But you can dynamically decide which header level you wanna you by passing in a prop.
function Header(props) {
const { level, style, children } = props
if (Number.isInteger(level) && level >= 1 && level <= 6) {
return React.createElement(`h${level}`, { className: styleMap[style] }, children)
} else {
throw Error('Invalid "level" prop, must be number 1-6')
}
}
// Usage:
<Header level={1} style='style5'>Main Title</Header>
You have to resort to the plain JS React.createElement syntax, cus JSX doesn't allow you to use dynamic value for component, it's simply syntax error to write <{whatever} /> in JSX.

Gutenberg blocks - processing server data within a block

I have a block that renders titles of all existing posts in the <Modal/>. I retrieve it from the server using <ServerSideRender/> (that returns plain html). I want to be able to choose one of the titles (ideally save it in the postTitle attribute) and display it in the block.
attributes: {
postTitle: {
type: 'string'
}
},
edit(props) {
const { attributes } = props;
const { postTitle } = props.attributes;
const MyModal = withState( {
isOpen: false,
} )
( ( { isOpen, setState } ) => (
<div>
<Button isDefault onClick={ () => setState( { isOpen: true } ) }>Choose</Button>
{ isOpen ?
<Modal onRequestClose={ () => setState( { isOpen: false } ) }>
<ServerSideRender
block="my-blocks/wordpress-title"
attributes={attributes}
/>
</Modal>
: null }
</div>
) );
return ([
<InspectorControls>
<div>
<strong>Choose Wordpress title:</strong>
<MyModal/>
</div>
</InspectorControls>,
]);
},
Is there any sensible way to retrieve data from a server, so it was possible to operate on it within a block?

How to add connect with todo list item component

All:
I am pretty new to redux and react-redux, I wonder if I design a todo item component(the element unit of a todolist) like
const todo => ({id, todocontent}) {
return <li key={id}>{todocontent}</li>
}
then how can I build that mapStateToProp function and use it with connect?
Say the todo list data is like :
{
title: "TodoList",
version:"1.0",
todolist: [
{id:"id1", todocontent: "todo 1"},
{id:"id2", todocontent: "todo 2"}
]
}
Something like this:
class YourComponent extends Component {
render () {
return (
<div>
{this.props.items.forEach((item) => {
return <todo id={item.id} todocontent={item.todocontent} />
})}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
items: state.<yourReducerName>.todolist
}
}
connect(mapStateToProps)(YourComponent)

render duplicate turn the page reactjs

I have the following code, which loads the information of the current user in the nav bar if the user is logged in or loads empty data if the user has not logged in. The code works for me, but I have a problem: when I go to any other page the nav is duplicated (specifically the rendernav function).
editprofile.js --> create the dispatch, and load the JSON
export const editProfile = (callback) => {
return function(dispatch) {
dispatch({type: 'LIST_USER_REQUEST'});
axios({
method: 'get',
url: 'https://gist.githubusercontent.com/anonymous/38c1444f753c70cf79ee980638a14de7/raw/34951eebfa006fea3db00fb492b491ac990c788e/vamos.json',
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
.then((response) => {
dispatch({type:'LIST_USER_SUCCESS', payload:response.data});
if (typeof callback === 'function') {
callback(null, response.data);
}
})
.catch((error) => {
dispatch({type:'LIST_USER_FAILURE'});
if (error.response.status == 401) {
browserHistory.push('login')
toastr.error(error.response.message, 'User')
}
if (typeof callback === 'function') {
callback(error.response.data, null)
}
})
}
}
EditProfileComponent.jsx -->created the component
export default class NavComponent extends Component {
render() {
return (
<table>
<thead>
<tr>
<th>SN</th>
<th>Email</th>
<th>created</th>
</tr>
</thead>
<tbody>
{this.renderSign()}
</tbody>
</table>
)
}
// --> this function duplicated, when I go to another page
renderSign() {
return this.props.allProfile.map((profile, index) => {
if (profile.status === 'SUCCESS') {
return (
<ul className="nav navbar-nav" key={index}>
<li className="dropdown user user-menu">
<a href="#" className="dropdown-toggle" data-toggle="dropdown">
<img src="img/perfil.jpg" className="user-image" alt="User Image" />
<span className="hidden-xs">{profile.user.email}</span>
</a>
</li>
</ul>
)
} else if (profile.status === 'FAIL') {
return (
<ul className="nav navbar-nav" key={index}>
<li><Link to='/sign_in'>Sign In</Link></li>
<li><Link to='/sign_up'>Sign Up</Link></li>
</ul>
)
}
}
}
Join the component with the service:
import { editProfile } from '../action/editProfile.js';
import NavComponent from '../component/editProfileComponent.jsx';
export default class EditProfileContainer extends Component {
componentDidMount() {
this.props.editProfile();
}
render () {
return (
<NavComponent allProfile={this.props.allProfile} />
);
}
}
function mapStateToProps(store) {
return {
allProfile: store.allProfile
};
}
function matchDispatchToProps(dispatch) {
return bindActionCreators(
{
editProfile:editProfile
},
dispatch
)
}
export default connect(mapStateToProps, matchDispatchToProps)(EditProfileContainer);
editProfileReducer --> the reducer
export const editProfileReducer = (state=[], action) => {
switch(action.type) {
case 'LIST_USER_REQUEST':
return state;
case 'LIST_USER_SUCCESS':
return state;
case 'LIST_USER_FAILURE':
return [...action.payload];
default:
return state;
}
}
Even though I don't see the whole code structure, it looks like you are using the wrong reducer. I see that you are using store.allProfile when you should be using store.editProfile. If the allProfile array has two items then you would have two <ul> tags when you use the .map function. If the allProfile array was intentionally intended to be used, then you should not use the .map function and instead just use just the first element of the array.

Resources