How to access the div element with a key in React JS? (react-dnd) - css

This is a small app created using react-beautiful-dnd. I want to add individual css element for different key values passed through this div element. However I am unable to access this specific div element for the specific key value in css.
<div key={key} className={"column"}>
<ProjectWrapper className="border">
<hr className="hr"></hr>
<h3 className="title">{data.title}</h3>
</ProjectWrapper>
The entire source code:
import React, {useState} from 'react';
import './App.css';
import {DragDropContext, Droppable, Draggable} from "react-beautiful-dnd";
import _ from "lodash";
import {v4} from "uuid";
const item = {
id: v4(),
name: "Clean the house"
}
const item2 = {
id: v4(),
name: "Wash the car"
}
function App() {
const [text, setText] = useState("")
const [state, setState] = useState({
//creating 3 columns
"todo": {
title: "Todo",
items: [item, item2]//temporary data valuesa
},
"in-progress": {
title: "In Progress",
items: []
},
"done": {
title: "Completed",
items: []
}
})
const handleDragEnd = ({destination, source}) => {
if (!destination) {
return
}
if (destination.index === source.index && destination.droppableId === source.droppableId) {
return
}
// Creating a copy of item before removing it from state
const itemCopy = {...state[source.droppableId].items[source.index]}
setState(prev => {
prev = {...prev}
// Remove from previous items array
prev[source.droppableId].items.splice(source.index, 1)
// Adding to new items array location
prev[destination.droppableId].items.splice(destination.index, 0, itemCopy)
return prev
})
}
const addItem = () => {
setState(prev => {
return {
...prev,
todo: {
title: "Todo",
items: [
{
id: v4(),
name: text
},
...prev.todo.items
]
}
}
})
setText("")
}
//the dragdropcontext is the wrapper for draging elements
//dropable is the certain area inside the column where u can drop items
//dragble are the items in the column to be dragged
//here {_.map(state, (data, key) => {
//the above function is used to return the data and key of all 3 elements mentioned under use state() above
//the key: returns todo,inprogress,and done
//where as the data returns all the values of the variables within each key
return (
<div className="App">
<div>
<input type="text" value={text} onChange={(e) => setText(e.target.value)}/>
<button onClick={addItem}>Add</button>
</div>
<DragDropContext onDragEnd={handleDragEnd}>
{_.map(state, (data, key) => {
return(
<div key={key} className={"column"}>
<h3>{data.title}</h3>
<Droppable droppableId={key}>
{(provided, snapshot) => {
return(
<div
ref={provided.innerRef}
{...provided.droppableProps}
key = {"Todo"}
className={"droppable-col"}
>
{data.items.map((el, index) => {
return(
<Draggable key={el.id} index={index} draggableId={el.id}>
{(provided, snapshot) => {
console.log(snapshot)
return(
<div
className={`item ${snapshot.isDragging && "dragging"}`}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
{el.name}
</div>
)
}}
</Draggable>
)
})}
{provided.placeholder}
</div>
)
}}
</Droppable>
</div>
)
})}
</DragDropContext>
</div>
);
}
export default App;

May I suggest using a utility like clsx
It will apply the specific class based on your value like so:
import clsx from 'clsx';
export default function App() {
const key = 'todo'; // or 'in-progress' or 'done'
return (
<div className={clsx('existingClass', key)}>
.
.
.
</div>
);
}
Which renders as <div class="existingClass todo">...</div>. You can configure your CSS classnames for the dynamic values. (in this case, todo)
// https://github.com/lukeed/clsx/blob/master/src/index.js
// MIT © Luke Edwards
function toVal(mix) {
var k, y, str='';
if (typeof mix === 'string' || typeof mix === 'number') {
str += mix;
} else if (typeof mix === 'object') {
if (Array.isArray(mix)) {
for (k=0; k < mix.length; k++) {
if (mix[k]) {
if (y = toVal(mix[k])) {
str && (str += ' ');
str += y;
}
}
}
} else {
for (k in mix) {
if (mix[k]) {
str && (str += ' ');
str += k;
}
}
}
}
return str;
}
function clsx() {
var i=0, tmp, x, str='';
while (i < arguments.length) {
if (tmp = arguments[i++]) {
if (x = toVal(tmp)) {
str && (str += ' ');
str += x
}
}
}
return str;
}
// https://github.com/lukeed/clsx/blob/master/src/index.js
// MIT © Luke Edwards
//-------------------
function App() {
const key = ['todo', 'in-progress', 'done'];
return (
<div className="App">
{key.map((k) => (
<span className={clsx('text', k)}>{k} - </span>
))}
</div>
);
}
ReactDOM.render(<App />, document.getElementById("react"));
.App {
font-family: sans-serif;
text-align: center;
}
.text{
font-size: 16px;
margin: 12px;
}
.todo {
color: orange;
}
.in-progress {
color: magenta;
}
.done {
color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Related

(Vue/Tailwindcss) I want to dynamically add tailwind styles to children with the help of computed function but it doesn't work as expected

<script setup lang="ts">
import { computed } from 'vue'
import type { Sizes, Colors as GlobalColors } from '#/types/global/global';
import type {MainColors} from './types'
const props = withDefaults(
defineProps<{
size?: Sizes
color?: GlobalColors
src?: string
alt?: string
width?: number
height?: number
text?: string
status?: MainColors
}>(),
{
size: 'large',
color: 'primary',
}
)
const sizes = computed(() => {
if (props.size === 'tiny') {
return ['h-4', 'w-4']
}
if (props.size === 'small') {
return ['h-6', 'w-6']
}
if (props.size === 'medium') {
return ['h-8', 'w-8']
}
if (props.size === 'big') {
return ['h-10', 'w-10']
}
if (props.size === 'large') {
return ['h-12', 'w-12']
}
if (props.size === 'huge') {
return ['h-16', 'w-16']
}
if (props.size === 'gigantic') {
return ['h-20', 'w-20']
}
else return ''
})
</script>
<template>
<div
class="
flex avatar-group [&>.avatar]:-mr-3"
:class="`
[&>.avatar]:${sizes[0]}
[&>.avatar]:${sizes[1]}`">
<slot />
</div>
</template>
If I write this:
class="[&>.avatar]:w-12 [&>.avatar]:h-12"
Then it's working just fine.
But if I pass props like 'large' or 'big' or anything less then it's only showing in styles like this:
So as far as i understand, the styles are not uploading correctly but the weirdest thing is that two of the props are working correctly that are - 'huge', 'gigantic'. And I have no idea why..

react HOC component not working after passed with dispatch and getstate from reduxjs

I have a piece of code down below work well before transformed to HOC component:
class Filters extends React.Component {
componentDidMount() {
const { store } = this.context;
this.unsubscribe = store.subscribe(() => this.forceUpdate());
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const { store } = this.context;
const currentFilter = store.getState().visibilityFilter;
const tags = ["All", "Active", "Completed"];
return (
<>
Show:{" "}
{tags.map((t, index) => {
if (t === currentFilter) {
return (
<a href="#">
{t + " "}
{index === 2 ? " " : ","}
</a>
);
}
return (
<a
onClick={() => {
store.dispatch({
type: "SET_VISIBILITY_FILTER",
filter: t
});
}}
>
{t + " "}
{index === 2 ? " " : ","}
</a>
);
})}
</>
);
}
}
Filters.contextTypes = {
store: React.PropTypes
};
I changed it into HOC component like below and its not working and not error notification.This code is from canonical tutorial todolist from reduxjs offical document.I check react offical document https://reactjs.org/docs/higher-order-components.html and did not find any exception which did not obey its rules.Does anyone know what is error?
const Originfilters = ({ currentFilter, handleClick = (f = f) }) => {
const tags = ["All", "Active", "Completed"];
return (
<>
Show:{" "}
{tags.map((t, index) => {
if (t === currentFilter) {
return (
<a href="#">
{t + " "}
{index === 2 ? " " : ","}
</a>
);
}
return (
<a
onClick={(t) => {
handleClick(t);
}}
>
{t + " "}
{index === 2 ? " " : ","}
</a>
);
})}
</>
);
};
class Filters extends React.Component {
componentDidMount() {
const { store } = this.context;
this.unsubscribe = store.subscribe(() => this.forceUpdate());
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const { store } = this.context;
return (
<Originfilters
currentFilter={store.getState().visibilityFilter}
handleClick={(filter) => {
store.dispatch({
type: "SET_VISIBILITY_FILTER",
filter
});
}}
/>
);
}
}
Filters.contextTypes = {
store: React.PropTypes
};
This logic is stale nowadays and cancel the question

ReactJS+MaterialUI v1 automatic nested list padding

I have nested lists and I don't know how many (recursive function). How to make them to get automatic padding?
The result I get is this
:
And I want to get this result:
If I add nested styles, they are the same for all nested lists. I need the padding to increase for each next level.
Can you help? (All comments are welcome!)
The code:
import React from 'react';
import {isNullOrUndefined} from "util";
import {TagNode} from "../interfaces/TagTree";
import HistoryDataUtility from "../../utilities/historyDataUtility";
import TreeUtility from "../../utilities/treeUtility";
import { createStyles } from '#material-ui/core/styles';
import { withStyles } from '#material-ui/core/styles';
import Checkbox from '#material-ui/core/Checkbox';
import Collapse from '#material-ui/core/Collapse';
import Icon from '#material-ui/core/Icon';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemSecondaryAction from '#material-ui/core/ListItemSecondaryAction';
import ListItemText from '#material-ui/core/ListItemText';
export interface TreeRendererProps {
itemList: TagNode[],
selectedTags: string[],
expandedFolders: string[],
onFolderToggle: any,
onTagToggle: any,
onSelectedFolderChange?: any,
selectedFolder?: string,
classes: {
root: string;
nested: string;
}
}
const styles = createStyles({
root: {
width: '100%',
},
nested: {
paddingLeft: 16
}
});
const TreeRenderer: React.StatelessComponent<TreeRendererProps> = (props) => {
const buildListItems = (item: TagNode,
selectedTags: string[],
onFolderToggle: any,
onTagToggle: any,
source: string) => {
let key = HistoryDataUtility.combineTag(source, item.objId);
let isExpanded = props.expandedFolders.indexOf(key) != -1;
let isSelected: boolean = props.selectedFolder === key ? true : false;
let children1: any, children2: any;
if(!TreeUtility.isTagItem(item)) {
children1 = item.children.filter(filterGroups).sort(sortOnTitle).map((child) => {
return buildListItems(child,
selectedTags,
onFolderToggle,
onTagToggle,
source);
}) || null;
children2 = item.children.filter(filterTags).sort(sortOnTitle).map((child) => {
return buildListItems(child,
selectedTags,
onFolderToggle,
onTagToggle,
source);
}) || null;
}
let collapseItem: any;
if(isExpanded && !isNullOrUndefined(item.children)) {
collapseItem =
<Collapse component="li" in={true} timeout="auto" unmountOnExit>
<List disablePadding>
{children1}
{children2}
</List>
</Collapse>
}
let key2 = "list" + Math.random().toString(36).substr(2, 9);
return (
!TreeUtility.isTagItem(item)
?
<div key={key2}>
<ListItem
button key={key}
className={props.classes.nested + " with-children"}
onClick={onFolderToggle.bind(this, key)}
>
<ListItemIcon>
{isExpanded ? <Icon>remove</Icon> : <Icon>add</Icon>}
</ListItemIcon>
<ListItemText primary={item.title} />
<ListItemSecondaryAction>
<Checkbox checked={isSelected} color="primary" onChange={props.onSelectedFolderChange} value={key} />
</ListItemSecondaryAction>
</ListItem>
{collapseItem}
</div>
:
<ListItem
button
className={props.classes.nested + " no-children" + ((selectedTags.indexOf(key) != -1 || selectedTags.indexOf(item.objId) != -1) ? " tagitem-activated" : "")}
key={key}
onClick={onTagToggle.bind(this, key)}
>
<ListItemIcon><Icon className="status">fiber_manual_record</Icon></ListItemIcon>
<ListItemText primary={item.title} />
</ListItem>
);
}
const filterGroups = (item: TagNode): boolean => {
return !TreeUtility.isTagItem(item);
}
const filterTags = (item: TagNode): boolean => {
return TreeUtility.isTagItem(item);
}
const sortOnTitle = (a: TagNode, b: TagNode) => {
var nameA = a.title.toUpperCase(); // ignore upper and lowercase
var nameB = b.title.toUpperCase(); // ignore upper and lowercase
if (nameA < nameB) {
return -1;
}
if (nameA > nameB) {
return 1;
}
// names must be equal
return 0;
}
const buildList = (items: TagNode[],
selectedTags: string[],
onFolderToggle: any,
onTagToggle: any) => {
let children1: any, children2: any;
children1 = items.filter(filterGroups).sort(sortOnTitle).map((item: TagNode) => {
let source = item.objId; //Use the top level group nodes id as source
return buildListItems(
item,
selectedTags,
onFolderToggle,
onTagToggle, source);
}) || null;
children2 = items.filter(filterTags).sort(sortOnTitle).map((item: TagNode) => {
return buildListItems(
item,
selectedTags,
onFolderToggle,
onTagToggle, null);
}) || null;
return (
<List>
{children1}
{children2}
</List>
)
}
let tree;
if (props.itemList.length > 0) {
if (props.itemList[0].children != undefined) {
tree = buildList(
props.itemList[0].children,
props.selectedTags,
props.onFolderToggle,
props.onTagToggle);
} else {
tree = <div>{props.itemList[0].name + ' : ' + props.itemList[0].title}</div>
}
} else {
tree = <div><h2 className="small">Model not found.</h2></div>;
}
return (<div>{tree}</div>);
}
export default withStyles(styles, { withTheme: true })(TreeRenderer);
Actually, the solution is amazingly simple. Just change this line:
<Collapse component="li" in={true} timeout="auto" unmountOnExit>
to this:
<Collapse component="li" in={true} timeout="auto" unmountOnExit style={{paddingLeft: '16px'}}>

Can't read property 'bind' of undefined

OS: Windows 10 Pro
apollo-client: 1.9.2
react-redux: 5.0.6
So, I'm attempting to read 'connect' a graphql resultset to redux but am receiving the above mentioned error message. My code is as follows:
import { connect } from 'react-redux';
class PhotoGrid extends React.Component {
render () {
const { data } = this.props;
const isNewPage = this.props.location.pathname.includes('new');
if (data.loading) {
return <p>Loading ...</p>;
}
if (data.error) {
return <p>{data.error.message}</p>;
}
return (
<div>
<div>Total record count: {data._allPostsesMeta.count}</div>
<div className="photo-grid">
{ data.allPostses.map( (post,i) => <Photo {...this.props} key={i} postIndexID={i} post={post} />) }
</div>
{isNewPage && (
<div>
<div onClick={() => this.previousPage()}>
Previous
</div>
<div onClick={() => this.nextPage()}>
Next
</div>
</div>
)}
</div>
);
}
};
const allPostsCommentsQuery = graphql(All_Posts_Comments_Query, {
options: ownProps => {
const page = parseInt(ownProps.match.params.page, 10);
const isNewPage = ownProps.location.pathname.includes('new');
const skip = isNewPage ? (page - 1) * parseInt(PRODUCTS_PER_PAGE) : 0;
const first = isNewPage ? parseInt(PRODUCTS_PER_PAGE) : parseInt(PRODUCTS_PER_PAGE);
const orderBy = isNewPage ? OrderBy : null;
fetchPolicy: 'network-only';
return {
variables: {
__offline__: true,
first,
skip,
orderBy,
},
}
},
});
export default connect(allPostsCommentsQuery)(PhotoGrid)
What am I overlooking here?

How to find unused style definitions in React?

Is there a way to automagically find unused style definitions in a React based project, like in this example?
Before:
const styles = StyleSheet.create({
name: {
color: '#e5e5e5'
}
});
const Hello = React.createClass({
render: function() {
return <Text style={styles.name}>Hello {this.props.name}</Text>;
}
});
After:
Developer has changed styles and "name" is not required any more. How can this kind of dead style code be automatically found?
const styles = StyleSheet.create({
name: { // Redundant now!
color: '#e5e5e5'
},
foo: {
color: '#e2e3b5'
}
});
const Hello = React.createClass({
render: function() {
return <Text style={styles.foo}>Hello {this.props.name}</Text>;
}
});
Possible solution
1) helpers.js
// helpers.js
import ReactTestUtils from 'react-addons-test-utils'
export function unusedStyles(component, styles) {
const renderer = ReactTestUtils.createRenderer();
renderer.render(component);
const rendered = renderer.getRenderOutput();
const myStylesInUse = stylesInUse(rendered);
return filterStyles(styles, myStylesInUse);
}
function stylesInUse(el) {
var arr = [];
function go(el) {
const { children, style } = el.props;
const childrenType = Object.prototype.toString.call(children);
style && arr.push(style)
if(childrenType === '[object Object]') {
go(children);
} else if(childrenType === '[object Array]') {
children.forEach(child => { go(child) });
}
}
go(el);
return arr;
}
function filterStyles(styles, compStyles) {
const arr = [];
for(let key in styles) {
const found = compStyles.find(item => item === styles[key]);
if(!found) arr.push(key)
}
return arr;
}
2) Component.js
import { unusedStyles } from './helpers';
const styles = StyleSheet.create({
one: {
color: 'one'
},
two: {
color: 'two'
},
three: {
color: 'three'
}
});
class Hello extends Component {
render() {
return (
<div style={styles.one}>
<div style={style.two}>Hello!</div>
</div>
)
}
}
// here you can test your styles
const myUnusedStyles = unusedStyles(<Hello />, styles)
// => ['three']
if(myUnusedStyles.length) {
console.log('UNUSED STYLES DETECTED', myUnusedStyles);
}
export default Hello

Resources