So i've written a simple TODO with drag and drop and add, all the cards are generated using a function , my second column has an id to it and I now want any task added or dragged to it to have a css line through. How would I go about targeting it using the id
<div class="listContainer" id="y0zitt47gfb">
My code:
P.S Does not use create-react-app , just brought in through CDN and also uses react-dnd
let { DragDropContext, Draggable, Droppable } = window.ReactBeautifulDnd;
let lists = {};
let storeLists = () => {
console.log("Writing Lists to Storage");
console.log(lists);
window.localStorage.setItem("listStore", JSON.stringify(lists));
console.log("Set Correctly");
lists = JSON.parse(localStorage.getItem("listStore"));
};
class Task extends React.Component {
deleteTask = () => {
let listId = this.props.listId;
let index = lists[listId]["tasks"].findIndex((task) => {
return task["taskId"] == this.props.inputTask["taskId"];
});
lists[this.props.listId]["tasks"].splice(index, 1);
storeLists();
this.props.refresh();
};
taskContentChanged = (event) => {
let listId = this.props.listId;
console.log(listId);
console.log(lists[listId]);
console.log(this.props.inputTask);
let index = lists[listId]["tasks"].findIndex((task) => {
return task["taskId"] == this.props.inputTask["taskId"];
});
console.log(index);
lists[listId]["tasks"][index]["taskDescription"] = event.target.innerText;
};
keyPress = (event) => {
if (event.key === "Enter" && event.target.className == "taskCardContent") {
storeLists();
event.target.blur();
this.props.refresh();
}
};
render = () => {
return (
<Draggable
draggableId={this.props.inputTask["taskId"]}
index={this.props.index}
>
{(provided) => {
return (
<div
class="taskCard"
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
id={this.props.inputTask["id"]}
>
<p
onKeyDown={this.keyPress}
onInput={this.taskContentChanged}
contentEditable="true"
class="taskCardContent"
>
{" "}
{this.props.inputTask["taskDescription"]}{" "}
</p>
<img
onClick={this.deleteTask}
class="deleteTaskIcon"
src="assets/delete.svg"
alt="Delete Task"
/>
</div>
);
}}
</Draggable>
);
};
}
class List extends React.Component {
addTask = () => {
let listId = this.props.inputList['listId'];
let newTaskInput = document.getElementById(listId + "Input");
if (newTaskInput.value != "") {
let newTask = {
taskId: Math.random().toString(36).substring(2, 15),
taskDescription: newTaskInput.value,
};
lists[listId]["tasks"].push(newTask);
storeLists();
}
newTaskInput.value = "";
this.props.refresh();
};
deleteList = () => {
let listId = this.props.inputList["listId"];
lists[listId] = undefined;
storeLists();
this.props.refresh();
};
listNameChanged = (event) => {
let listId = this.props.inputList["listId"];
lists[listId]["listName"] = event.target.innerText;
};
keyPress = (event) => {
if (event.target.className == "newListItemInput" && event.key == "Enter") {
this.addTask();
}
if (event.target.className == "listName" && event.key == "Enter") {
event.preventDefault();
event.stopPropagation();
event.target.blur();
storeLists();
this.props.refresh();
}
};
render = () => {
let list = this.props.inputList;
return (
<div class="body">
<div class="newListItem">
<input
onKeyDown={this.keyPress}
id={list["listId"] + "Input"}
type="text"
class="newListItemInput"
placeholder="Add To List"
/>
<img
class="addTaskIcon"
onClick={this.addTask}
src="assets/submit.svg"
alt="Add Task"
/>
</div>
<div class="listContainer" id={this.props.id}>
<div class="listHeader">
<h4 class="listName">{list["listName"]} </h4>
</div>
<Droppable droppableId={list["listId"]}>
{(provided) => {
return (
<div
class="taskList"
{...provided.droppableProps}
ref={provided.innerRef}
>
{Object.values(list["tasks"]).map((task, index) => {
return (
<Task
index={index}
inputTask={task}
listId={list["listId"]}
refresh={this.props.refresh}
/>
);
})}
{provided.placeholder}
</div>
);
}}
</Droppable>
</div>
</div>
);
};
}
class App extends React.Component {
state = {
appLists: lists,
};
onDragEnd = (result) => {
console.log(result);
if (result.reason === "DROP" && result.destination) {
let originListId = result.source.droppableId;
let originIndex = result.source.index;
let destinationListId = result.destination.droppableId;
let destinationIndex = result.destination.index;
let movedTask = lists[originListId]["tasks"][originIndex];
lists[originListId]["tasks"].splice(originIndex, 1);
lists[destinationListId]["tasks"].splice(destinationIndex, 0, movedTask);
}
storeLists();
this.refreshApp();
};
refreshApp = () => {
console.log("Refreshing...");
this.setState({
appLists: lists,
});
};
render = () => {
return (
<div id="app">
<DragDropContext onDragEnd={this.onDragEnd}>
{Object.values(this.state.appLists).map((list) => {
return (
<List
id={list["listId"]}
inputList={list}
refresh={this.refreshApp}
/>
);
})}
</DragDropContext>
</div>
);
};
}
let startUp = () => {
if (
localStorage.getItem("listStore") &&
!["undefined", "null"].includes(localStorage.getItem("listStore"))
) {
lists = JSON.parse(localStorage.getItem("listStore"));
} else {
console.log("Creating List in Storage");
lists = {
f0ziqq7gfn: {
listId: "f0ziqq7gfn",
listName: "To Do",
tasks: [
{ taskId: "fk8vs85v0wh", taskDescription: "Watch Regular Show" },
{ taskId: "kd3ve56f5fh", taskDescription: "" },
],
},
y0zitt47gfb: {
listId: "y0zitt47gfb",
listName: "Complete",
tasks: [
{ taskId: "v0zigt47whc", taskDescription: "Create React Todo" },
{ taskId: "b0zi5t4rwhc", taskDescription: "Somehow get a PS5" },
],
},
};
storeLists();
}
ReactDOM.render(<App />, document.body);
};
startUp();
Brainfart moment, just used that ID standardly , BURN OUT
Related
CodeSandbox
function disabledRow on click does not send changed data to child component.
I don't see any error in the code. Can't figure out how to display data in child component.
const UsersPage = () => {
const [dataState, setDataState] = useState<DataType[]>(dataInitial);
const [idState, setIdState] = useState<number[]>();
const disabledRow = () => {
if (idState) {
dataInitial.forEach((item) => {
if (idState.some((id) => id === item.id)) {
item.activeStatus = false;
item.date = <StopOutlined />;
item.status = "Not active";
}
});
return setDataState(dataState);
}
};
const idRow = (id: number[]) => {
return setIdState(id);
};
console.log("Hello", dataState);
return (
<div>
<div className={"wrapper"}>
<Button onClick={disabledRow}>
<StopOutlined /> Deactivate
</Button>
</div>
<UsersTable data={dataState} idRow={idRow} />
</div>
);
};
This is my App.js
console.log(customer) shows the data on the console here, so I think there is no problem on my API.
let client = null;
let customer_id = null;
var customer = null;
const getCustomerId = () => {
client.get('ticket.requester.id').then(
function(data) {
customer_id = data['ticket.requester.id'].toString();
}
);
var settings = {
url:'/api/sunshine/objects/records?type=Customer',
type:'GET',
dataType: 'json',
};
client.request(settings).then(
function(data) {
var jsonCount = Object.keys(data.data).length;
var x = 0;
console.log(customer_id);
while(x < jsonCount) {
var cust = data.data[x];
if (cust.attributes.CustomerID == customer_id) {
customer = data.data[x];
// console.log(customer);
}
x = x + 1;
}
console.log(customer);
},
function(response) {
console.error(response.responseText);
}
);
}
const App = () => {
const [expandedSections, setExpandedSections] = useState([]);
const [expandedSections2, setExpandedSections2] = useState([]);
useEffect(() => {
client = window.ZAFClient.init();
getCustomerId();
}, []);
return (
<Row justifyContent="center">
<Col className="outer_column">
<Accordion className="first_accordion"
level={2}
expandedSections={expandedSections}
onChange={index => {
if (expandedSections.includes(index)) {
setExpandedSections(expandedSections.filter(n => n !== index));
} else {
setExpandedSections([index]);
}
}}
>
<Accordion.Section>
<Accordion.Header>
<Accordion.Label>Customer Management</Accordion.Label>
</Accordion.Header>
<Accordion.Panel>
<Row justifyContent="center">
<Col className="inner_column">
<Accordion
isCompact
level={5}
isExpandable
className="second_accordion"
>
<Accordion.Section>
<Accordion.Header>
<Accordion.Label>Customer Information</Accordion.Label>
</Accordion.Header>
<Accordion.Panel className="accordion_panel">
<Display jsonData = {JsonData} tryData = {customer}/>
</Accordion.Panel>
</Accordion.Section>
</Accordion>
</Col>
</Row>
</Accordion.Panel>
</Accordion.Section>
</Accordion>
</Col>
</Row>
)
}
export default App;
This is my Display.js:
function withMyHook(Component) {
return function WrappedComponent(props) {
const myHookValue = useZafClient();
return <Component {...props} myHookValue={myHookValue} />;
}
}
class Display extends React.Component {
constructor(props) {
super(props);
this.state = {
fields: this.props.jsonData.data[0].attributes,
errors: {},
customers: [],
flag: (this.props.jsonData.data[0].attributes.CustomerID === "") ? 'register' : 'view'
};
}
handleChange(field, e) {
let fields = this.state.fields;
fields[field] = e.target.value;
this.setState({ fields });
}
render() {
console.log("trydata");
console.log(this.props.tryData);
return(
<div>
{Object.keys(this.props.jsonData.data[0].attributes).map((key, i) => (
<p key={i}>
<span>{key}
<input value={this.state.fields[key] || ''}
placeholder={key}
disabled = {(this.state.flag === 'view') ? "disabled" : "" }
onChange={this.handleChange.bind(this,key)} /></span>
</p>
))}
{(this.state.flag === "register") ?
<Button onClick={() => this.setState({flag: 'view'})}> Register </Button> :
null
}
{(this.state.flag === "view") ?
<Button onClick={() => this.setState({flag: 'update'})}> Edit </Button> :
null
}
{(this.state.flag === "update") ?
<Button onClick={() => this.setState({flag: 'view'})}> Submit </Button> :
null
}
</div>
)
}
};
export default withMyHook(Display);
As you can see.. the API call on my App.js is being passed through tryData = {customer}, my problem is the data won't be pass to Display.js, not until I open the Accordion Customer Information.. you can see on the display.js that I am using this.props.jsonData.data[0].attributes instead of this.props.tryData.attributes, because I get type error Props error
Try to put variables inside of the App component that you pass as a props value
const App =()=>
{
let client=null,
....
}
I am working on react js app where I used "react-dragula" to drag and drop the list items. I am showing the preview of the child component html inside the parent wrapper component. After dropping an element my html is not render properly. I have no idea whether it is because of Dragula or there is some other issue.
After dropping the list item I am updating list values according to the element index and updating the state and the re rendering the child component. But it shows me old html it's not re rendering the html of child component using updated props return by parent component.
Here is my code::
class App extends React.Component {
drake = null;
socialContainers = [];
/** lifecycle method */
componentDidMount() {
this.drake = Dragula(this.socialContainers, {
isContainer: function (el) {
return (el.id === 'social-container');
},
moves: function (el, source, handle, sibling) {
return (handle.id === "socialSortBtn");
},
accepts: function (el, target, source, sibling) {
return (target.id === 'social-container' && source.id === 'social-container');
},
});
this.drake.on('drop', (el, target, source, sibling) => {
el.style.cursor = "grab";
let oldIndex = el.dataset.index
let type = el.dataset.type
let iconIndx = Array.prototype.indexOf.call(target.children, el);
let targetIndex = iconIndx > 0 ? iconIndx : 0;
if (type) {
let content = {}
content.reOrder = true;
content.oldIndex = oldIndex;
this.props.callback(content, 'content', targetIndex)
}
});
this.drake.on('drag', (el, source) => {
el.style.cursor="grabbing";
})
}
updateLinkText(val, index) {
const { data, callback } = this.props;
let textParsedHtml = new DOMParser().parseFromString(data.content[index].text, 'text/html');
if (textParsedHtml.getElementsByTagName("a")[0]) {
textParsedHtml.getElementsByTagName("a")[0].innerHTML = val;
}
let content = {}
content.changeLinkTxt = true
content.linkText = val
content.linkTextHtml = textParsedHtml.body.innerHTML
//update content
callback(content, 'content', index)
}
onSelectShareOpt(selectedVal, selectedIndx, event) {
event.stopPropagation();
let socialObj = socialShareArr.find((obj) => obj.value === selectedVal)
if (socialObj) {
let htmlObj = this.getHTMLObj(socialObj);
let content = {}
content.active_social_icons = socialObj
if(htmlObj) { content.content = htmlObj }
// update content
this.props.callback(content, 'content', selectedIndx)
}
}
isIconDisabled = (data, val) => {
let found = false;
found = data.some(function (obj) {
if (obj.value === val) {
return true;
}
return false;
});
return found;
}
renderSocialIcons(dragulaDecoratorRef) {
const { data } = this.props;
let socialIcons = data.activeIcons;
if (!socialIcons) {
return null
}
return (
<Fragment>
{socialIcons && socialIcons.length > 0 && socialIcons.map((activeIcon, index) => (
<li key={index} data-index={index} data-type={activeIcon.value} className="mb-30">
<div className="mr-2">
<button data-toggle="tooltip" title="Drag to reorder" className="btn btn-primary btn-sm btn-icon" id="dragBtn"><span id="socialSortBtn" className="material-icons m-0">drag_indicator</span>
</button>
</div>
<div className="mb-30">
<img className="mr-2" src={activeIcon.icon} alt="social-icon" width="36" height="36" />
<FormControl
value={activeIcon.value}
as="select"
onChange={e => this.onSelectShareOpt(e.target.value, index, e)}
custom
size=''
>
{socialShareArr && socialShareArr.map((option, index) => (
<option
key={index}
disabled={this.isIconDisabled(socialIcons, option.value)}
value={option.value}
>
{option.label}
</option>
))}
</FormControl>
</div>
<Form.Group>
<Form.Label>Link Text</Form.Label>
<TextInput
value={activeIcon.linkText}
onChange={(e) => this.updateLinkText(e.target.value, index)}
wrapperClass="mx-10 mb-0"
/>
</Form.Group>
</li>
))}
</Fragment>
);
}
// main function
render() {
const { data } = this.props;
const dragulaDecoratorRef = (componentBackingInstance) => {
if (componentBackingInstance) {
this.socialContainers.push(componentBackingInstance)
}
};
return (
<Fragment>
{data &&
<AppCard>
<Card.Body>
<div className="social-opt-container">
<ul id='social-container' ref={dragulaDecoratorRef}>
{this.renderSocialIcons(dragulaDecoratorRef)}
</ul>
</div>
</Card.Body>
</AppCard>
}
</Fragment>
)
}
}
export { App }
I have also tried to remove innerHTML of ""and then return new structure but in this case it returns nothing in html. Please check this once why this issue occurring.
I am trying to use buttons to filter data from JSON. Whenever a button is clicked, it will update the state with filtered JSON data. It will then return the list with the updated data. Right now I have four buttons to filter the four different "types" but the code is not working at all.
import React, { useState, useEffect } from 'react';
import * as moment from 'moment';
import PollCard from './PollCard'
function PollList() {
const [polls, setPolls] = useState(null);
const [loading, setLoading] = useState(false);
const range = 30
var dateRange = moment().subtract(range, 'days').calendar();
async function fetchMyAPI() {
let response = await fetch(url)
const json = await response.json()
var data = json.filter(e => Date.parse(e.endDate) >= Date.parse(dateRange))
setPolls(data.reverse())
setLoading(true);
}
useEffect(() => {
fetchMyAPI();
}, [])
if (!loading) {
return ("Loading...")
}
var A = polls.filter(e => e.type === "A")
var B = polls.filter(e => e.type === "B")
var C = polls.filter(e => e.type === "C")
function showA() {
setPolls(A)
}
function showB() {
setPolls(B)
}
function showC() {
setPolls(C)
}
return (
<div className="polls">
<button onClick={showA()}>A</button>
<button onClick={showB()}>B</button>
<button onClick={showC()}>C</button>
{
polls && polls.map((poll) => (
<div key={poll.id}>
<PollCard poll={poll} />
<hr style={{ opacity: '.1' }} />
</div>
))
}
</div>
);
}
export default PollList;
You need two arrays to properly filter your data, when you click one of the buttons, it overwrites the original data. Change <button onClick={showA()}>A</button> to <button onClick={() => showA()}>A</button>
// get data from api, won't change
const [polls, setPolls] = useState(null);
// used for displaying polls and filtering
const [filteredPolls, setfilteredPolls] = useState(null)
Filtering data
var A = polls.filter(e => e.type === "A")
var B = polls.filter(e => e.type === "B")
var C = polls.filter(e => e.type === "C")
function showA() {
setfilteredPolls(A)
}
function showB() {
setfilteredPolls(B)
}
function showC() {
setfilteredPolls(C)
}
Displaying data
return (
<div className="polls">
<button onClick={() => showA()}>A</button>
<button onClick={() => showB()}>B</button>
<button onClick={() => showC()}>C</button>
{
filteredPolls && filteredPolls.map((poll) => (
<div key={poll.id}>
<PollCard poll={poll} />
<hr style={{ opacity: '.1' }} />
</div>
))
}
</div>
);
DEMO
<script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0-alpha.2/umd/react.production.min.js"></script>
<script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
class PollCard extends React.Component {
render() {
return (
<div>{`${this.props.poll.name} - ${this.props.poll.type}`}</div>
)
}
}
const dpolls = [
{
id: 4,
type: 'C',
name: 'Test 1'
},
{
id: 6,
type: 'B',
name: 'Test 2'
},
{
id: 7,
type: 'A',
name: 'Test 3'
},
{
id: 8,
type: 'A',
name: 'Test 9'
},
{
id: 17,
type: 'B',
name: 'Test 39'
}
]
function PollList() {
const [polls, setPolls] = React.useState(null);
const [filteredPolls, setfilteredPolls] = React.useState(null)
const [loading, setLoading] = React.useState(false);
const range = 30
// var dateRange = moment().subtract(range, 'days').calendar();
async function fetchMyAPI() {
let response = await fetch('https://api.themoviedb.org/3/movie/upcoming?api_key=81f382d33088c6d52099a62eab51d967&language=en-US&page=1')
const json = await response.json()
// var data = json.filter(e => Date.parse(e.endDate) >= Date.parse(dateRange))
setPolls(dpolls);
setfilteredPolls(dpolls.filter(e => e.type === "A"));
setLoading(true);
}
React.useEffect(() => {
fetchMyAPI();
}, [])
if (!loading) {
return ("Loading...")
}
var A = polls.filter(e => e.type === "A")
var B = polls.filter(e => e.type === "B")
var C = polls.filter(e => e.type === "C")
function showA() {
setfilteredPolls(A)
}
function showB() {
setfilteredPolls(B)
}
function showC() {
setfilteredPolls(C)
}
function removeFiter() {
setfilteredPolls(polls);
}
return (
<div className="polls">
<button onClick={() => showA()}>A</button>
<button onClick={() => showB()}>B</button>
<button onClick={() => showC()}>C</button>
<button onClick={() => removeFiter()}>Remove Filter</button>
{
filteredPolls && filteredPolls.map((poll) => (
<div key={poll.id}>
<PollCard poll={poll} />
<hr style={{ opacity: '.1' }} />
</div>
))
}
</div>
);
}
class App extends React.Component {
constructor() {
super();
this.state = {
name: 'React'
};
}
render() {
return (
<div>
<PollList />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
</script>
I am aware of similar threads here, but any of them still can't help me.
I'm trying to pass deleteItem() function from parent component to onClick argument in grandson component.
Please, look at components and tell me what is wrong, what should I change to access this function in grandson component?
Parent - https://codeshare.io/2E39oO
Child - https://codeshare.io/5XnwN8
Grandson - https://codeshare.io/5z9JXE
Here are the two things I spotted
misspelling in deleteHandler (already mentioned)
the button was disabled, so it wouldn't trigger an event
Example I ended up with
class ToDo extends Component {
constructor(props) {
super(props);
this.state = {
list: [
{
title: "Cup cleaning",
todo: "Wash and take away the Kurzhiy's cup from WC"
},
{
title: "Smoking rollton",
todo: "Do some rollton and cigarettes"
},
{
title: "Curious dream",
todo: "Build a time machine"
}
],
title: "",
todo: ""
};
}
createNewToDoItem = () => {
this.setState(({ list, title, todo }) => ({
list: [
...list,
{
title,
todo
}
],
title: "",
todo: ""
}));
};
handleKeyPress = e => {
if (e.target.value !== "") {
if (e.key === "Enter") {
this.createNewToDoItem();
}
}
};
handleTitleInput = e => {
this.setState({
title: e.target.value
});
};
handleTodoInput = e => {
this.setState({
todo: e.target.value
});
};
deleteItem(indexToDelete) {
console.log("HERE");
this.setState(({ list }) => ({
list: list.filter((toDo, index) => index !== indexToDelete)
}));
}
editItem = (i, updTitle, updToDo) => {
let arr = this.state.list;
arr[i].title = updTitle;
arr[i].todo = updToDo;
this.setState({ list: arr });
};
eachToDo = (item, i) => {
return (
<ToDoItem
key={i}
title={item.title}
todo={item.todo}
deleteItem={this.deleteItem.bind(this, i)}
editItem={this.editItem.bind(this, i)}
/>
);
};
render() {
return (
<div className="ToDo">
<h1 className="ToDo-Header" />
<div className="ToDo-Container">
<div className="ToDo-Content">
{this.state.list.map(this.eachToDo)}
</div>
<div>
<input
type="text"
placeholder="Enter new title"
value={this.state.title}
onChange={this.handleTitleInput}
onKeyPress={this.handleKeyPress}
/>
<input
type="text"
placeholder="Enter new todo"
value={this.state.todo}
onChange={this.handleTodoInput}
onKeyPress={this.handleKeyPress}
/>
{/* <AddButton addHandler={this.createNewToDoItem} /> */}
</div>
</div>
</div>
);
}
}
class ToDoItem extends Component {
constructor(props) {
super(props);
this.state = {
editMode: false
};
}
edit = () => {
this.setState({ editMode: true });
};
save = () => {
let updTitle = this.refs.newTitle.value;
let updToDo = this.refs.newToDo.value;
this.props.editItem(updTitle, updToDo);
this.setState({
editMode: false
});
};
renderNormal = () => {
return (
<div className="ToDoItem">
<p className="ToDoItem-Text">{this.props.title}</p>
<p className="ToDoItem-Text">{this.props.todo}</p>
{/* <EditButton editHandler={this.edit} /> */}
<FloatingActionButtons deleteHandler={this.props.deleteItem} />
{/* <button className="ToDoItem-Button" id="editbtn" onClick={this.edit}>✍</button> */}
{/* <button className="ToDoItem-Button" id="delbtn" onClick={this.props.deleteItem}>−</button> */}
</div>
);
};
renderEdit = () => {
return (
<div className="ToDoItem">
<textarea ref="newTitle" defaultValue={this.props.title} />
<textarea ref="newToDo" defaultValue={this.props.todo} />
<button onClick={this.save} className="ToDoItem-Button" id="savebtn">
💾
</button>
</div>
);
};
render() {
if (this.state.editMode) {
return this.renderEdit();
} else {
return this.renderNormal();
}
}
}
const styles = theme => ({
button: {
margin: theme.spacing.unit
}
});
function FloatingActionButtons(props) {
return (
<div>
<Button variant="fab" aria-label="Delete" onClick={props.deleteHandler}>
Delete
</Button>
</div>
);
}
FloatingActionButtons.propTypes = {
classes: PropTypes.object.isRequired
};