Display Content based on user selection - javascript

I'm developing a Yes or No question based user interface, where based on the user selection I need to load specific content and also allow users to move to previously selected question.
Below is the Data structure from which I need to load the questions.
const data = {
preQuestions: {
landingPage: {
heading: 'This is the ladning page',
buttons: {
yesButton: {
text: 'yes',
action: '1A',
},
noButton: {
text: 'No',
action: '1B',
},
},
},
'1A': {
heading: 'This is the 1A Component',
buttons: {
yesButton: {
text: 'yes',
action: '1C',
},
noButton: {
text: 'No',
action: '1D',
},
},
},
'1B': {
heading: 'This is the 1B Component',
buttons: {
yesButton: {
text: 'yes',
action: '1E',
},
noButton: {
text: 'No',
action: '1F',
},
},
},
'1C': {
heading: 'This is the 1C Component',
buttons: {
yesButton: {
text: 'yes',
action: '1G',
},
noButton: {
text: 'No',
action: '1H',
},
},
},
'1D': {
heading: 'This is the 1C Component',
buttons: {
yesButton: {
text: 'yes',
action: '1I',
},
noButton: {
text: 'No',
action: '1J',
},
},
},
},
};
Below is my logic to render the questions on user action.
const content = data.preQuestions;
const loadNextCompnent = (actionId) => {
console.log(actionId);
return renderQustionComponent(actionId);
};
const renderQustionComponent = (key) => {
console.log(content[key]);
return (
<div id={key}>
Previous question
<h1>{content[key].heading}</h1>
<button
onClick={() =>
loadNextCompnent(content[key].buttons.yesButton.action)
}
>
{content[key].buttons.yesButton.text}{' '}
</button>
<button
onClick={() => loadNextCompnent(content[key].buttons.noButton.action)}
>
{content[key].buttons.noButton.text}{' '}
</button>
</div>
);
};
Problem is, when user clicks on the yes or no button nothing happens.
How to do I move to the previous question with smooth scroll?
Below is the stackblitz link. Please guide me.
https://stackblitz.com/edit/react-ts-eugpwn?file=App.tsx

Hello i have created the following example with react-router-dom so you can have a look in the following demo:
https://react-ts-31wshc.stackblitz.io
App.tsx
import * as React from 'react';
import './style.css';
import RenderQuestionComponent from './RenderQuestionComponent';
import Test from './Test';
import Page1 from './Page1';
import { BrowserRouter, Routes, Route, useNavigate } from 'react-router-dom';
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Page1 />} />
<Route path="/test" element={<Test />} />
</Routes>
</BrowserRouter>
);
}
Page1.tsx
import * as React from 'react';
import { useNavigate } from 'react-router-dom';
export default function Page1() {
const navigate = useNavigate();
return (
<div>
<h1>THIS IS THE PAGE 1 </h1>
<button
onClick={() => {
navigate('/test'); // you will go one page back
}}
>
GO TO TEST PAGE{' '}
</button>
</div>
);
}
Test.tsx
import * as React from 'react';
import { useNavigate } from 'react-router-dom';
export default function Test() {
const navigate = useNavigate();
return (
<div>
<h1>TESTING </h1>
<button
onClick={() => {
navigate(-1); // you will go one page back
}}
>
GO BACK{' '}
</button>
</div>
);
}
You can have a look on this website to learn more about react-router-dom:
https://reactrouter.com/en/main - current version 6.4.1

Your logic for calling keys from objects are fine, the problem is that you do not have sufficient logic to update dom and see the changes. Here is the working version which I updated it with states
https://react-ts-z8clfj.stackblitz.io
import * as React from 'react';
import './style.css';
export default function App() {
const data = {
preQuestions: {
landingPage: {
heading: 'This is the ladning page',
buttons: {
yesButton: {
text: 'yes',
action: '1A',
},
noButton: {
text: 'No',
action: '1B',
},
},
},
'1A': {
heading: 'This is the 1A Component',
buttons: {
yesButton: {
text: 'yes',
action: '1C',
},
noButton: {
text: 'No',
action: '1D',
},
},
},
'1B': {
heading: 'This is the 1B Component',
buttons: {
yesButton: {
text: 'yes',
action: '1C',
},
noButton: {
text: 'No',
action: '1D',
},
},
},
'1C': {
heading: 'This is the 1C Component',
buttons: {
yesButton: {
text: 'yes',
action: '1A',
},
noButton: {
text: 'No',
action: '1B',
},
},
},
'1D': {
heading: 'This is the 1D Component',
buttons: {
yesButton: {
text: 'yes',
action: '1B',
},
noButton: {
text: 'No',
action: '1C',
},
},
},
},
};
const [active, setActive] = React.useState<any>(
data.preQuestions['landingPage']
);
const content = data.preQuestions;
const loadNextCompnent = (actionId) => {
setActive(actionId);
};
const renderQustionComponent = () => {
console.log('I am active', active);
return (
<div>
Previous question
<h1>{active?.heading}</h1>
<button
onClick={() =>
loadNextCompnent(content[active?.buttons?.yesButton?.action])
}
>
{active.buttons.yesButton.text}{' '}
</button>
<button
onClick={() =>
loadNextCompnent(content[active?.buttons?.noButton?.action])
}
>
{active.buttons.noButton.text}{' '}
</button>
</div>
);
};
return <div>{renderQustionComponent()}</div>;
}

Related

How to add link in sidebar of ant design (version >=4.20.0)

I searched and tried different methods to apply links in the sidebar of ant design. But in vain.
In the last version, we were applying links like this
<Menu.SubMenu key="SubMenu" icon={<SettingOutlined />}>
<Menu.Item key="two" icon={<AppstoreOutlined />}>
<Link to="">Navigation Two</Link>
</Menu.Item>
</Menu.SubMenu>
But now they have changed it to function-based. Something like this
<Menu
mode="inline"
openKeys={openKeys}
onOpenChange={onOpenChange}
style={{ width: 256 }}
items={items}
/>
Now I tried a few methods to apply links to code. But they are not working properly. If anybody can help then, please help.
antd link:https://ant.design/components/menu/
Well what you can do is add navigation in label key in items array and then your code will be working same as before
const items: MenuProps['items'] = [
{
label: 'Navigation One',
key: 'mail',
icon: <MailOutlined />,
},
{
label: (
<a href="https://ant.design" target="_blank" rel="noopener noreferrer">
Navigation Two
</a>
),
key: 'alipay',
},
];
Use this react component
Add <Link /> component
Displaying 3 levels
Can check permission
Full custom setup
import React, { useContext, useState } from 'react'
import { HomeOutlined, SafetyOutlined } from '#ant-design/icons';
import { Menu, Layout } from 'antd';
import { Link } from "react-router-dom";
import { AuthContext } from 'context/auth-context'
import { PrivateLayoutContext } from 'context/private-layout-context'
const { Sider } = Layout;
function LeftSideBar() {
const { permissions } = useContext(AuthContext);
const { leftSideBarCollapsed } = useContext(PrivateLayoutContext);
const items = [
{
label: 'Home',
path: '/',
key: 'home',
icon: <HomeOutlined />,
},
{
label: 'Access Control',
key: 'access-control',
icon: <SafetyOutlined />,
children: [
{
label: 'User',
path: '/access-control/user',
key: 'user',
permission: 'user list',
},
{
label: 'Role',
path: '/access-control/role',
key: 'role',
permission: 'role list',
}
]
},
{
label: 'Configuration',
key: 'configuration',
icon: <SafetyOutlined />,
children: [
{
label: 'Inventory',
key: 'inventory',
children: [
{
label: 'Inventory 1',
path: '/configuration/inventory',
key: 'inventory-1',
permission: 'inventory list',
},
{
label: 'Inventory 2',
path: '/configuration/inventory-2',
key: 'inventory-2',
permission: 'inventory list',
},
]
},
{
label: 'Customer',
path: '/configuration/customer',
key: 'customer',
}
]
},
]
const onClick = (e) => {
console.log('click', e);
};
return (
<Sider trigger={null} collapsible collapsed={leftSideBarCollapsed} >
{/* This menu is designed to be used for displaying 3 levels */}
<Menu
onClick={(e) => onClick(e)}
style={{
height: '100vh',
}}
mode="vertical"
items={
// level 1 = root level
items.map((l1_item, index) => {
// console.log(l1_item?.permission, permissions, permissions?.includes(l1_item?.permission));
return {
...l1_item,
label: <Link to={l1_item?.path}>{l1_item?.label}</Link>,
// level 2
children: l1_item?.children?.map((l2_item, l2_index) => {
// if (l2_item) has permission then check permission exist in permissions array, otherwise return
let return_status = 0;
if (l2_item?.permission) {
if (permissions?.includes(l2_item?.permission)) {
return_status = 1;
}
}
else {
return_status = 1;
}
return return_status && {
...l2_item,
label: <Link to={l2_item?.path}>{l2_item?.label}</Link>,
// level 3
children: l2_item?.children?.map((l3_item, l3_index) => {
// if (l3_item) has permission then check permission exist in permissions array, otherwise return
let return_status = 0;
if (l3_item?.permission) {
if (permissions?.includes(l3_item?.permission)) {
return_status = 1;
}
}
else {
return_status = 1;
}
return return_status && {
...l3_item,
label: <Link to={l3_item?.path}>{l3_item?.label}</Link>
}
})
}
})
}
})
}
>
</Menu>
</Sider>
)
};
export default LeftSideBar;

Insert Icon on AntD Column Table based on Data Device

I have a problem here please anyone can help me?
I have a trouble with Ant Design table here, in Source column here I want to insert Icon, I already have inserted an icon but I want the Icon change based on the data of the device, if device Id = 1 it would be "Human Icon" that will showed up, but if device id = 2 it would be "Computer Icon" that will show.
Here's the code :
import React, { useEffect, useState } from 'react';
import { Card, Col, Divider, Layout, Row, Table, Tag } from 'antd';
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { Content, Footer, Header } from 'antd/lib/layout/layout';
import 'antd/dist/antd.css';
import '../Page/Dashboard.css';
import { LaptopOutlined, UserAddOutlined, UserOutlined } from '#ant-design/icons';
const columns = [
{
title: "No",
dataIndex: "id",
key: "id",
},
{
title: 'Device ID',
dataIndex: "dev_id",
key: "dev_id",
},
{
title: 'Message ID',
dataIndex: "msg_id",
key: "msg_id",
},
{
title: 'Time Stamp',
dataIndex: "time_stamp",
key: "time_stamp",
},
{
title: 'RFID',
dataIndex: "rfid",
key: "rfid",
},
{
title: 'Data Hewan',
dataIndex: "animals",
key: "animals",
},
{
title: 'Weight (kg)',
dataIndex: "weight",
key: "weight",
},
{
title: 'Temperature (Celcius)',
dataIndex: "temp",
key: "temp",
},
{
title: 'Tags',
dataIndex: "tags",
key: "tags",
render: (_, { tags }) => (
<>
{tags.map((tag) => {
let color = 'geekblue';
if (tag === 'Invalid Data') {
color = 'volcano';
} else {
color = 'geekblue';
}
return (
<Tag color={color} key={tag}>
{tag.toUpperCase()}
</Tag>
);
})}
</>
),
},
{
title: 'Source',
dataIndex: "",
key: "",
render: text => <UserOutlined />
}
];
export default function Dashboard(){
return (
<Table column={columns} dataSource={dataQurban} />
)}
Thank you I hope you'll could help me!
{
title: 'Source',
dataIndex: "",
key: "",
render: (_: any, record: any) => record.dev_id === 1 ? <UserOutlined /> : <ComputerIcon />
}
The code above is the direct solution.
You could aslo define a variable if you want to optimize the code.
const iconObj = {
1: <Icon1 />,
2: <Icon2 />,
3: <Icon3 />,
// ...
}
// ...
{
title: 'Source',
dataIndex: "",
key: "",
render: (_, record) => iconObj[record.dev_id]
}

Can not hide add-button like isEditHiden/isDeleteHiden in material table conditionally

In material-table there is option for hiding edit and delete button conditionally like
<MaterialTable
/// other props
editable={
10 > 5 && {
isEditHidden: () => !10 > 5, // This is condition
isDeleteHidden: () => !10 > 5, // This is condition
onRowAdd: newData =>
}),
onRowUpdate: (newData, oldData) =>
}),
onRowDelete: oldData =>
})
}
}
/>
if isEditHidden or isDeleteHidden is true those button hide. I want to hide add button (beside search icon) also. But i couldn't find any option. Is there any option?
You need to remove editable props and actions props for custom actions if needed and then can use hidden/disabled property to hide/disable action button.
import React from "react";
import MaterialTable from "material-table";
export default function DisableFieldEditable() {
const { useState } = React;
const [columns, setColumns] = useState([
{ title: "Name", field: "name", editable: "onUpdate" },
{ title: "Surname", field: "surname", editable: "never" },
{ title: "Birth Year", field: "birthYear", type: "numeric" },
{
title: "Birth Place",
field: "birthCity",
lookup: { 34: "İstanbul", 63: "Şanlıurfa" }
}
]);
const [data, setData] = useState([
{ name: "Mehmet", surname: "Baran", birthYear: 1987, birthCity: 63 },
{ name: "Zerya Betül", surname: "Baran", birthYear: 2017, birthCity: 34 }
]);
return (
<MaterialTable
title="Disable Field Editable Preview"
columns={columns}
data={data}
actions={[
{
icon: "add",
tooltip: "Add User",
hidden: 10 < 5,
isFreeAction: true,
onClick: (event, rowData) => {
// Perform add operation
}
},
{
icon: 'edit',
tooltip: 'Edit User',
hidden: true,
onClick: (event, rowData) => {
// Perform edit operation
}
},
{
icon: 'delete',
tooltip: 'Delete User',
disabled: true,
onClick: (event, rowData) => {
// Perform delete operation
}
}
]}
/>
);
}

How to render a material-table using a JSON that comes from a API response?

I'm new on ReactJS and this is my first page that I created, but I'm having some problems with set variables.
What I need is fill the variable table.data with the values that comes from const response = await api.get('/users') and render the table with this values when page loads.
I have the following code:
import React, { useState, useEffect } from 'react';
import { Fade } from "#material-ui/core";
import MaterialTable from 'material-table';
import { makeStyles } from '#material-ui/core/styles';
import api from '../../services/api.js';
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1,
width: '70%',
margin: 'auto',
marginTop: 20,
boxShadow: '0px 0px 8px 0px rgba(0,0,0,0.4)'
}
}));
function User(props) {
const classes = useStyles();
const [checked, setChecked] = useState(false);
let table = {
data: [
{ name: "Patrick Mahomes", sector: "Quaterback", email: "patrick#nfl.com", tel: "1234" },
{ name: "Tom Brady", sector: "Quaterback", email: "tom#nfl.com", tel: "5678" },
{ name: "Julio Jones", sector: "Wide Receiver", email: "julio#nfl.com", tel: "9876" }
]
}
let config = {
columns: [
{ title: 'Name', field: 'name' },
{ title: 'Sector', field: 'sector' },
{ title: 'E-mail', field: 'email'},
{ title: 'Tel', field: 'tel'}
],
actions: [
{ icon: 'create', tooltip: 'Edit', onClick: (rowData) => alert('Edit')},
{ icon: 'lock', tooltip: 'Block', onClick: (rowData) => alert('Block')},
{ icon: 'delete', tooltip: 'Delete', onClick: (rowData) => alert('Delete')},
{ icon: 'visibility', tooltip: 'Access', onClick: (rowData) => alert('Access')},
{ icon: "add_box", tooltip: "Add", position: "toolbar", onClick: () => { alert('Add') } }
],
options: {
headerStyle: { color: 'rgba(0, 0, 0, 0.54)' },
actionsColumnIndex: -1,
exportButton: true,
paging: true,
pageSize: 10,
pageSizeOptions: [],
paginationType: 'normal'
},
localization: {
body: {
emptyDataSourceMessage: 'No data'
},
toolbar: {
searchTooltip: 'Search',
searchPlaceholder: 'Search',
exportTitle: 'Export'
},
pagination: {
labelRowsSelect: 'Lines',
labelDisplayedRows: '{from} to {to} for {count} itens',
firstTooltip: 'First',
previousTooltip: 'Previous',
nextTooltip: 'Next',
lastTooltip: 'Last'
},
header: {
actions: 'Actions'
}
}
}
useEffect(() => {
setChecked(prev => !prev);
async function loadUsers() {
const response = await api.get('/users');
table.data = response.data;
}
loadUsers();
}, [])
return (
<>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<Fade in={checked} style={{ transitionDelay: checked ? '300ms' : '0ms' }}>
<div className={classes.root}>
<MaterialTable editable={config.editable} options={config.options} localization={config.localization} title="Usuários" columns={config.columns} data={table.data} actions={config.actions}></MaterialTable>
</div>
</Fade>
</>
);
}
export default User;
The previous example will show 3 users that I fixed on variable table.data with 4 columns (name, sector, email, tel).
In a functional component, each render is really a new function call. So any variables you declare inside the component and destroyed and re-created. This means that table is set back to your initial value each render. Even if your useEffect is setting it correctly after the first render, it will just be reset on the next.
This is what state is for: to keep track of variables between renders. Replace your let table, with a new state hook.
const [table, setTable] = useState({
data: [
{ name: "Patrick Mahomes", sector: "Quaterback", email: "patrick#nfl.com", tel: "1234" },
{ name: "Tom Brady", sector: "Quaterback", email: "tom#nfl.com", tel: "5678" },
{ name: "Julio Jones", sector: "Wide Receiver", email: "julio#nfl.com", tel: "9876" }
]
});
Then use it like this:
useEffect(() => {
setChecked(prev => !prev);
async function loadUsers() {
const response = await api.get('/users');
setTable(prev => ({...prev, data: response.data});
}
loadUsers();
}, [])
Since table.data is not a state variable, it is regenerated as it was declared originally every time the component renders, meaning that by the time it arrives as a prop to your component it will always be the same value (when you change the value of table.data in useEffect it is too late). You need to change table.data to a state variable, and then in your useEffect hook you can update the value of table.data to the value of response.data. This will cause the component to be re-rendered but with the updated value.
Here's an example of how you might do that:
import React, { useState, useEffect } from 'react';
import { Fade } from "#material-ui/core";
import MaterialTable from 'material-table';
import { makeStyles } from '#material-ui/core/styles';
import api from '../../services/api.js';
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1,
width: '70%',
margin: 'auto',
marginTop: 20,
boxShadow: '0px 0px 8px 0px rgba(0,0,0,0.4)'
}
}));
function User(props) {
const classes = useStyles();
const [checked, setChecked] = useState(false);
const [tableData, setTableData] = useState([]);
let config = {
columns: [
{ title: 'Name', field: 'name' },
{ title: 'Sector', field: 'sector' },
{ title: 'E-mail', field: 'email'},
{ title: 'Tel', field: 'tel'}
],
actions: [
{ icon: 'create', tooltip: 'Edit', onClick: (rowData) => alert('Edit')},
{ icon: 'lock', tooltip: 'Block', onClick: (rowData) => alert('Block')},
{ icon: 'delete', tooltip: 'Delete', onClick: (rowData) => alert('Delete')},
{ icon: 'visibility', tooltip: 'Access', onClick: (rowData) => alert('Access')},
{ icon: "add_box", tooltip: "Add", position: "toolbar", onClick: () => { alert('Add') } }
],
options: {
headerStyle: { color: 'rgba(0, 0, 0, 0.54)' },
actionsColumnIndex: -1,
exportButton: true,
paging: true,
pageSize: 10,
pageSizeOptions: [],
paginationType: 'normal'
},
localization: {
body: {
emptyDataSourceMessage: 'No data'
},
toolbar: {
searchTooltip: 'Search',
searchPlaceholder: 'Search',
exportTitle: 'Export'
},
pagination: {
labelRowsSelect: 'Lines',
labelDisplayedRows: '{from} to {to} for {count} itens',
firstTooltip: 'First',
previousTooltip: 'Previous',
nextTooltip: 'Next',
lastTooltip: 'Last'
},
header: {
actions: 'Actions'
}
}
}
useEffect(() => {
setChecked(prev => !prev);
async function loadUsers() {
const response = await api.get('/users');
setTableData(response.data);
}
loadUsers();
}, [])
return (
<>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<Fade in={checked} style={{ transitionDelay: checked ? '300ms' : '0ms' }}>
<div className={classes.root}>
<MaterialTable editable={config.editable} options={config.options} localization={config.localization} title="Usuários" columns={config.columns} data={tableData} actions={config.actions}></MaterialTable>
</div>
</Fade>
</>
);
}
export default User;

Getting setState is not defined no-undef error using React hooks

I am just getting started with React. So I generated a new React app with npx create-react-app . and it generated me a what I think is functional React hooks components. I guess this is the 2020 version.
But I ran into a problem when I tried to update my state. I basically wanted to toggle the completed property of the selected todo item. But when I called the setTodos method it gave me this error:
index.js:1 ./src/App.js
Line 27:5: 'setTodos' is not defined no-undef
import React, { useState } from 'react'
import Todos from './components/Todos.js'
function App()
{
const [todos, setTodos] = useState([
{ id: 1, title: 'First todo item', completed: false },
{ id: 2, title: 'Second todo item', completed: true },
{ id: 3, title: 'Third todo item', completed: false },
])
return (
<div>
<Todos
todos={todos}
markComplete={ (event, todo) => markComplete(event, todo) }
/>
</div>
)
}
function markComplete(event, todo)
{
// this works
console.log('You clicked todo with id: ' + todo.id + ' and title: ' + todo.title)
// setTodos is not defined...?
setTodos({
id: 1,
title: 'Test',
completed: true,
})
}
export default App
setTodos is only in scope within the function it is defined in, in this case the App component. Move markComplete into your component.
import React, { useState } from 'react'
import Todos from './components/Todos.js'
function App() {
const [todos, setTodos] = useState([
{ id: 1, title: 'First todo item', completed: false },
{ id: 2, title: 'Second todo item', completed: true },
{ id: 3, title: 'Third todo item', completed: false },
]);
function markComplete(event, todo) {
console.log('You clicked todo with id: ' + todo.id + ' and title: ' + todo.title)
setTodos({
id: 1,
title: 'Test',
completed: true,
})
}
return (
<div>
<Todos
todos={todos}
markComplete={ (event, todo) => markComplete(event, todo) }
/>
</div>
)
}
export default App
Put markComplete in the same function scope as setTodos
import React, { useState } from 'react'
import Todos from './components/Todos.js'
function App()
{
function markComplete(event, todo)
{
setTodos({
id: 1,
title: 'Test',
completed: true,
})
}
const [todos, setTodos] = useState([
{ id: 1, title: 'First todo item', completed: false },
{ id: 2, title: 'Second todo item', completed: true },
{ id: 3, title: 'Third todo item', completed: false },
])
return (
<div>
<Todos
todos={todos}
markComplete={ (event, todo) => markComplete(event, todo) }
/>
</div>
)
}
export default App
State
state use only inside a component.
state change a value inside a component.
So you must put the function markComplete inside your component App.
import React, { useState } from 'react'
import Todos from './components/Todos.js'
function App()
{
const [todos, setTodos] = useState([
{ id: 1, title: 'First todo item', completed: false },
{ id: 2, title: 'Second todo item', completed: true },
{ id: 3, title: 'Third todo item', completed: false },
])
const markComplete = (event, todo) =>
{
setTodos({
id: 1,
title: 'Test',
completed: true,
})
}
return (
<div>
<Todos
todos={todos}
markComplete={ (event, todo) => markComplete(event, todo) }
/>
</div>
)
}
export default App

Categories

Resources