Syncrohize react conditional rendering and typescript - reactjs

In this component I trigger post loading using postsActions.getPost('1') and put it into the redux store. useSelector catches it and triggers PostPage rerender, now with header and button with onClickUse function attached that uses post.header along with the post object that it uses:
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { postsActions } from '../../store/Posts';
import styles from './PostPage.module.scss';
const PostPage = () => {
const dispatch = useDispatch();
const post = useSelector((state) => state.post);
const onClickUse = () => {
console.log(`See? I am used only when post is rendered`);
console.log(`So it's fine if I use it here: ${post.header}`);
}
useEffect(() => {
dispatch(postsActions.getPost('1'));
}, []);
return (
<div className={styles.container}>
{
post &&
<div>
<h1>{post.header}</h1>
<button onClick={onClickUse}>Click me</button>
</div>
}
</div>
);
};
export default PostPage;
The problem is that typescript yells inside onClickUse at me that post can be undefined. How do I then synchronize React conditional rendering functionality and typescript without hacks from this question, like !, as etc.

You can inline
<div className={styles.container}>
{
post &&
<div>
<h1>{post.header}</h1>
<button onClick={() => {
console.log(`See? I am used only when post is rendered`);
console.log(`So it's fine if I use it here: ${post.header}`);
}}>Click me</button>
</div>
}
</div>
or if you don't want inline functions in render, you should create a component with not falsy post in props and conditionally render it.
Typescript (in handler) knows nothing about render logic in your example

Related

Button don't passing data to another component. React

I have a component which is a button. Then in another component i am looping trough concerts and using this button to redirect to booking page but after clicking my data is not passed.
This is my button component:
import React from "react";
export const BookBtn = (props) => {
return (
<div>
<button
className="bookBtn"
style={{ backgroundColor: props.color }}
// onClick={props.func}
>
{props.text}
</button>
</div>
);
};
BookBtn.defaultProps = {
text: "Unavailable",
};
export default BookBtn;
Here is the button in my main component where I try to click
<a href={"/concert/" + concert.id} data={concert}>
<BookBtn text="Book a ticket" />
</a>
Here is my component where i try to redirect to and retrive my data.
import React from "react";
import { useEffect, useState } from "react";
import axios from "axios";
export const Book = (data) => {
const [concerts, setConcerts] = useState([]);
const [tickets, setTickets] = useState([]);
useEffect(() => {
const loadConcerts = async () => {
const resConcerts = await axios.get("data/concerts");
const tickets = await axios.get("/data/tickets");
};
});
return (
<div>
Booking page
<h1>{data.name}</h1>
</div>
);
};
UPDATE:
I wrapped my button in anchor tag and now i am able to redirect but still can't pass data.
Final Update
Allright, i managed to pass my data using useLocation hook.
Problem is solved.
I'd suggest using react-router to do the redirection or routing instead of anchor tags as they cause a refresh.
Use the Link tag from react-router and pass the concert state along with it!
Have a look at this https://reactrouter.com/en/main/components/link.

How to reference from iframe to web service with react.js

We have to add eventListener from iframe to real service(button)
i can handle only iframe(react.js) and we cannot handle both of service due to cross-domain issue.
So i just tried with createRef in react.js but i can't reference outside of iframe.
import { createRef, useEffect } from "react";
const App = props => {
const selectChildReference = createRef();
useEffect(() => {
// This thing will be undefined
console.log(
selectChildReference?.current?.ownerDocument?.defaultView?.document?.getElementsByClassName(
"OgETmrvExa"
)
);
}, [selectChildReference]);
return (
<div ref={selectChildReference} className="parent">
<div className="app__main">
<h3>React Project</h3>
</div>
</div>
);
};
export default App;
Try the useRef hook instead of the createRef and add your listener methods onto the selectChildReference.current.

React Hooks Calling GET Request On Modal Popup

Thanks in advance for any help you can provide. I am trying to create a Modal is react and call a get request to load details of a task.
I have most of it working (I think), but essentially what I have done is createa custom Modal Hook that toggles two modals.
The second of the two modals is meant to open a task and render the task details in a form for editing but I am unable to get it working.
Here is the useModal hook:
import { useState } from "react";
const useModal = () => {
const [isShowing, setIsShowing] = useState(false);
const [secondModalIsShowing, secondModalSetIsShowing] = useState(false);
function toggle() {
setIsShowing(!isShowing);
}
function secondToggle() {
secondModalSetIsShowing(!secondModalIsShowing);
console.log("clicked");
}
return {
isShowing,
toggle,
secondModalIsShowing,
secondToggle,
};
};
export default useModal;
I then call the function for the secondToggle which fires the code below to render the modal. Now as you may see I have to comment out the section where it calls getTask() with match.params.id, as well as the component that is then meant to be rendered in the modal.
If I don't do that I get an error message with the following " Line 23:5: Expected an assignment or function call and instead saw an expression no-unused-expressions"
import React, { Fragment, useEffect, useState } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import TaskItem from "../tasks/task-item/TaskItem";
import { getTask } from "../../actions/task";
import ReactDOM from "react-dom";
import "./Modal.styles.scss";
import "../dashboard/Dashboard.styles.scss";
import Task from "../task/Task";
import TaskEdit from "../task/TaskEdit";
const TaskModal = ({
getTask,
task: { task, loading },
match,
secondModalIsShowing,
hide,
}) => {
const [displayEdit, toggleDisplayEdit] = useState(false);
useEffect(() => {
getTask();
// match.params.id;
}, [getTask]);
return secondModalIsShowing
? ReactDOM.createPortal(
<React.Fragment>
<button
type="submit"
value="toggle"
onClick={() => toggleDisplayEdit(!displayEdit)}
>
Show/Edit
</button>
{(displayEdit && <TaskItem task={task} />) || (
<div>{/* <TaskEdit /> */}</div>
)}
<div className="modal-overlay" />
<div
className="modal-wrapper"
aria-modal
aria-hidden
tabIndex={-1}
role="dialog"
>
<div className="modal">
<div className="modal-header">
Add New Task
<button
type="button"
className="modal-header__button"
data-dismiss="modal"
aria-label="Close"
onClick={hide}
>
<span aria-hidden="true">×</span>
</button>
</div>
</div>
</div>
</React.Fragment>,
document.body
)
: null;
};
Now if I render this EditTask component outside the modal as a normal component it works correctly. I can also get the modal to render when it's not trying to display the EditTask component.
As a result, I think it's related to the Route path and passing the response to the TaskModal component? When I click the modal to open I cant get it to render the URL with the task ID and therefore I cant render the details of the task in the modal.
<Route path="/taskedit/:id" component={TaskModal} />
For context, I think this guide comes close to solving my issue (https://blog.logrocket.com/building-a-modal-module-for-react-with-react-router/) but I am not familiar with working with class-based components and when I try and convert to functional-based components I'm running into even more issues.
Thanks in advance for any insight you can provide as I keep trying to work through this.
Paul
The first issue I am seeing is you have to pass the task id to TaskModal component
<Route path="/taskedit/:id"
render={(props) => <TaskModal {...props} />}>
</Route>
This will make the task id available as property in TaskModal.
Then in the TaskModal, fetch like below
let taskid = prop.match.params.id;

How to use react-hooks with redux and immutable js?

I am using react, redux with immutable js. I am facing a problem when I use useEffect or similar hooks with non-primitive data that effect is running even though it hasn't changed as I am using hoc recommended by redux docs.
Does anyone has a solution or best practice for using immutable js and redux with react hooks?
This is my parent component.
import { useSelector, useDispatch } from "react-redux";
import { setValue, getValue, getData } from "./store";
import "./styles.css";
import Child from "./Child";
export default function App() {
const data = useSelector(getData);
const value = useSelector(getValue);
const dispatch = useDispatch();
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={() => dispatch(setValue(value + 1))}>
Change Value
</button>
<Child value={value} data={data} />
</div>
);
}
This is child component which is wrapped with toJS hoc
import { toJS } from "./hoc";
function Child(props) {
useEffect(() => {
// this runs even if only props.value has changed.
console.log(props.data, "changed");
}, [props.data]);
return <div>{props.value}</div>;
}
export default toJS(Child);
Reproducible example:

How to refactor react hooks nested function properly

I have a function component which uses two states and both are changed based on event triggered.
I've read on react docs that is a bad idea to change states in nested function or condition. I also seen some examples using useEffects, but I have no clear idea how to properly refactor this.
here is my entire component:
import React, { useState, useEffect } from 'react'
import './App.css'
import AppHeader from '../app-header'
import AppFooter from '../app-footer'
import SearchInput from '../search-input'
import Stats from '../stats'
import Chart from '../chart'
import { getBundleInfoAPI } from '../../services/hostApi'
import 'react-loader-spinner/dist/loader/css/react-spinner-loader.css'
import Loader from 'react-loader-spinner'
function App() {
const [isSpinnerVisible, setSpinnerVisible] = useState(false)
const [bundleData, setBundleData] = useState({})
const _handleOnItemSelected = (item) => {
if (item && item.package && item.package.name && item.package.version) {
setSpinnerVisible(true)
getBundleInfoAPI(item.package.name, item.package.version)
.then(resposeData => setBundleData(resposeData))
.finally(() => setSpinnerVisible(false))
} else {
// TODO - implement an error handler component?
console.warn('WARNING: The selected bundle does not have name or version!')
}
}
return (
<div className="app">
<AppHeader />
<main>
<SearchInput onItemSelected={_handleOnItemSelected} />
<div className="app-main-inner-container">
<Loader type="Triangle" color="#00BFFF" height={200} width={200} visible={isSpinnerVisible} />
{!isSpinnerVisible &&
<div className="app-stats-chart-container">
<section className="app-stats-container"><Stats size={bundleData.stats} /></section>
<section className="app-chart-container"><Chart bundleChartData={bundleData.chart} /></section>
</div>
}
</div>
</main>
<AppFooter />
</div>
)
}
export default App
Docs section you are referring to means you must not put line with useState inside of nested functions, conditions, loops.
Calling setter returned by hook is definitely fine and correct.
This is fine, you are showing the loading screen when starting fetch and then hiding it when the fetch is done... no refactoring needed

Resources