React Class is throwing a compile error regading function classs using TypeScript - javascript

I have a react object that I am trying to convert from a fucntion to a class in order to save the state and bind certain functions that I want to pass to child components. I am getting an error that causes it ot not compile.
React Component that I am trying to create with the state and functions that will be passed into the WidgetTile objects.
import React from "react";
...
export default class WidgetToolbar extends React.Component{
constructor(props){
super(props)
this.state={
targetBox:null,
};
this.dragStart = this.dragStart.bind(this);
this.dragEnd = this.dragEnd.bind(this);
this.drop = this.drop.bind(this);
};
const isOpen = useBehavior(mainStore.isWidgetToolbarOpen);
const themeClass = useBehavior(mainStore.themeClass);
const userDashboards = useBehavior(dashboardStore.userDashboards);
const [filter, setFilter] = useState("");
const [sortOrder, setSortOrder] = useState<SortOrder>("asc");
const userWidgets = useMemo(() => {
let _userWidgets = values(userDashboards.widgets).filter((w) => w.widget.isVisible);
if (sortOrder === "asc") {
_userWidgets.sort((a, b) => a.widget.title.localeCompare(b.widget.title));
} else {
_userWidgets.sort((a, b) => b.widget.title.localeCompare(a.widget.title));
}
if (!isBlank(filter)) {
_userWidgets = _userWidgets.filter((row) => {
return row.widget.title.toLowerCase().includes(filter.toLocaleLowerCase());
});
}
return _userWidgets;
}, [userDashboards, sortOrder, filter]);
dragStart = (e) => {
// this is initiated whenthe drag starts.
console.log(e.target.id);
console.log('drag start');
this.setState({
targetbox: true,
selectedId: e.target.id
});
this.createPopup();
e.dataTransfer.setData("text", e.target.id);
}
dragEnd = (e) => {
// the following is activated when the drag is ended
console.log('ended drag');
this.setState({
targetBox:null
});
this.destroyPopup();
}
drop = (e) => {
console.log("end drag dt: " + e.dataTransfer.getData("text"));
console.log("end drag t:" + e.target.id);
console.log('set start: ' + this.state.iterating);
}
createPopup = () => {
console.log("create Popup");
}
destroyPopup = () => {
console.log("destroy popup");
}
render(){
return(
<Overlay
isOpen={isOpen}
hasBackdrop={false}
canOutsideClickClose={true}
canEscapeKeyClose={true}
onClose={mainStore.closeWidgetToolbar}
className={Classes.OVERLAY_SCROLL_CONTAINER}
>
<div className={classNames(styles.toolbar, className, themeClass)} data-element-id="widgets-dialog">
<h3 className={styles.toolbarTitle}>Widgets</h3>
<div className={styles.toolbarMenu}>
<InputGroup
placeholder="Search..."
leftIcon="search"
round={true}
// TODO - Implement mainstore widget filter
// onChange={handleStringChange(this.mainStore.setWidgetFilter)}
value={filter}
onChange={handleStringChange(setFilter)}
data-element-id="widget-search-field"
/>
<SortButton order={sortOrder} onClick={setSortOrder} />
<Button minimal icon="pin" />
<Button minimal icon="cross" onClick={mainStore.closeWidgetToolbar} />
</div>
<hr />
<div className={Classes.DIALOG_BODY}>
<div className={styles.buttonBar}>
<Button text="Prev" icon="caret-left" small={true} disabled={true} />
<span className={styles.currentPage}>Page 1</span>
<Button text="Next" icon="caret-right" small={true} disabled={true} />
</div>
<ul className={styles.widgetList}>
{userWidgets.map((userWidget) => (
<li key={userWidget.id}>
<UserWidgetTile
userWidget={userWidget}
dragStart={this.dragStart}
dragEnd={this.dragEnd}
drop={this.drop}/>
</li>
))}
</ul>
</div>
</div>
</Overlay>
)
};
};
The compile error that I am getting is the following:
./src/components/widget-toolbar/WidgetToolbar.tsx
Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: C:\Users\ojandali\Documents\dev\ozone1\ozone-framework-client\packages\application\src\components\widget-toolbar\WidgetToolbar.tsx: Unexpected token (35:10)
33 |
34 |
> 35 | const isOpen = useBehavior(mainStore.isWidgetToolbarOpen);
| ^
36 | const themeClass = useBehavior(mainStore.themeClass);
37 |
38 | const userDashboards = useBehavior(dashboardStore.userDashboards);
at Object.raise (C:\Users\ojandali\Documents\dev\ozone1\ozone-framework-client\packages\application\node_modules\#babel\parser\lib\index.js:3851:17)
at Object.unexpected (C:\Users\ojandali\Documents\dev\ozone1\ozone-framework-client\packages\application\node_modules\#babel\parser\lib\index.js:5167:16)

constructor(props) {
...
}; // <- semicolon here is invalid syntax in class
// v- const is also invalid syntax in class
const isOpen
Basically you got all wrong with ES6 class syntax. Please look up and get yourself familiar with that syntax.

Related

Callback set in the state variable of Context Provider will be undefined

I have referred other questions asked here by OPs but none seems to work for me. I have one layout and in that layout there is one toolbar which I am using to submit forms. Now to make that happen I using one FormProvider which is a Context.Provider (wraps layout component) with a state variable which stores function callback to submit a form. Now in the form component (which is loaded conditionally) I am using this setState func from context to assign form submit callback and in the toolbar using state variable from context to call that as a function. The problem I am facing is I always get state variable from context undefined. These are the snippets from my code.
FormProvider
type FormContextProps = {
setOnSubmit: (callable: Function | undefined) => void
assignOnSubmit: Dispatch<any>
setOnCancel: (callable: Function | undefined) => void
submit: (e: any) => void
cancel: () => void
}
const initAuthContextPropsState = {
setOnSubmit: () => { },
assignOnSubmit: () => { },
setOnCancel: () => { },
submit: (e: any) => { },
cancel: () => { },
}
const FormContext = createContext<FormContextProps>(initAuthContextPropsState)
const useTForm = () => {
return useContext(FormContext)
}
const FormProvider: FC = ({ children }) => {
const [onSubmit, assignOnSubmit] = useState<Function | undefined>()
const [onCancel, assignOnCancel] = useState<Function | undefined>()
const setOnSubmit = (callable: Function | undefined) => {
console.log('setOnSubmit', callable)
assignOnSubmit(callable)
console.log('setOnSubmit after', onSubmit)
}
const setOnCancel = (callable: Function | undefined) => {
assignOnCancel(callable)
}
useEffect(() => {
console.log("changed onSubmit"); // this hook is called only on first render
}, [onSubmit])
const submit = (e: any) => {
console.log('form submited', onSubmit) // this is always undefined when I click on save button on toolbar
if (onSubmit) onSubmit(e)
}
const cancel = () => {
if (onCancel) onCancel()
}
return (
<FormContext.Provider value={{ setOnSubmit, assignOnSubmit, setOnCancel, submit, cancel }}>
{children}
</FormContext.Provider>
)
}
Toolbar
const FormToolbar: FC = () => {
const {classes} = useLayout()
const {submit, cancel} = useTForm()
const submitForm = (e: any) => submit(e)
return (
<div className='toolbar' id='kt_toolbar'>
{/* begin::Container */}
<div
id='kt_toolbar_container'
className={clsx(classes.toolbarContainer.join(' '), 'd-flex flex-stack')}
>
<DefaultTitle />
{/* begin::Actions */}
<div className='py-1 d-flex align-items-center'>
{/* begin::Button */}
<button
className='btn btn-sm btn-primary me-4'
onClick={submitForm}
>
Save
</button>
<button
className='btn btn-sm btn-primary'
onClick={cancel}
>
Cancel
</button>
{/* end::Button */}
</div>
{/* end::Actions */}
</div>
{/* end::Container */}
</div>
)
}
EditForm.tsx
const EditForm: React.FC<Props> = () = {
const form = useRef() as React.MutableRefObject<HTMLFormElement>
const { setOnSubmit, assignOnSubmit } = useTForm()
useLayoutEffect(() => {
setOnSubmit(() => { form?.current.dispatchEvent(new Event('submit')) });
console.log('Form changed', form)
}, [form])
return (
<form onSubmit={formik.handleSubmit} ref={form}>
// ...
</form>
);
}
Main Component
function StaffManagement({ user, selectedLanguageId, idMenu }: Props) {
const [editing, setEditing]: [any, Function] = useState(null)
return (
<div className='row'>
<div className="col-lg-4">
<ModuleItemList
className='card-xxl-stretch mb-xl-3'
edit={setEditing}
/>
</div>
<div className="col-lg-8">
{editing && <EditForm
userId={user.id}
menuId={idMenu}
/>}
</div>
</div>
)
}
When using setState, we can do it in two ways,
setState(newState); // directly pass the new state
setState((currentState) => newState); // return the new state from a callback fn
That mean setState can accept a plain state value or a callback function which will return the new state that need to be set as the new state.
When you say,
const setOnSubmit = (callable: Function | undefined) => {
// callable => () => { form?.current.dispatchEvent(new Event('submit')) }
assignOnSubmit(callable)
}
Here React thinks you used the setState(assignOnSubmit) in the 2nd way I mentioned above, so react will call your callback and execute the form?.current.dispatchEvent(new Event('submit')). Since your callable function returns nothing, undefined will assigned to your onSubmit state.
So if you really need to store this function in a state, you have to do it as,
const setOnSubmit = (callable: Function | undefined) => {
assignOnSubmit(() => callable) // React will call the callback and return callable
}
Few Other Tips
Also do not use useLayoutEffect for this task. You can use useEffect and imrpove the performance of your application.
memoize the provider data, otherwise you will trigger unwanted re renders.
const data = useMemo(() => ({
setOnSubmit, assignOnSubmit, setOnCancel, submit, cancel
}), [submit, cancel, setOnCancel, assignOnSubmit, setOnSubmit])
return (
<FormContext.Provider value={data}>
{children}
</FormContext.Provider>
)
State updates are asynchronous. So you can't expect to console.log log the latest state
const setOnSubmit = (callable: Function | undefined) => {
console.log('setOnSubmit', callable)
assignOnSubmit(() => callable)
console.log('setOnSubmit after', onSubmit) // this won't log the latest as assignOnSubmit is async
}
you need to make <FormProvider as a parent of your Toolbar and EditForm to make your context working properly. Based on your code I don't see where you put the <FormProvider, so I'm guessing that you need to put it on your Main Component
Main Component
function StaffManagement({ user, selectedLanguageId, idMenu }: Props) {
const [editing, setEditing]: [any, Function] = useState(null)
return (
<FormProvider>
<div className='row'>
<div className="col-lg-4">
<ModuleItemList
className='card-xxl-stretch mb-xl-3'
edit={setEditing}
/>
</div>
<div className="col-lg-8">
{editing && <EditForm
userId={user.id}
menuId={idMenu}
/>}
</div>
</div>
</FormProvider>
)

What may cause a function to not be recognized as a function in React?

I'm starting studying React and I was following this YouTube tutorial of a TO DO LIST using React.
https://www.youtube.com/watch?v=E1E08i2UJGI
My form loads perfectly, but if I write something and press any button I get the message: "completedTask is not a function". The same goes for buttons that call a function 'removeTask' and 'setEdit'.
I don't understand the reason I'm getting such error message. In the tutorial it works when the buttons are clicked. I've read in some forums that it would be related to the fact that you can't use map on Objects (non-array elements), but I didn't understand it very well and I don't know how to fix it. And the most mysterious parte: why does his code work and mine do not?
Could anybody please explain it?
Obs1: I found in another post that return tasks.tasks.map((task, index) solved the problem for 'task.map is not a function' error message in TASK.JS. I'm using it instead of return tasks.map((task, index) but I also didn't understant the reason.
Obs2: I don't think it makes any difference for the error message, but I used the button tag instead using React Icons as the video suggests.
=== TASKLIST.JS ===
import React, { useState } from 'react'
import Task from './Task'
import TaskForm from './TaskForm'
function TaskList() {
const [tasks, setTasks] = useState([]);
const addTask = task => {
if(!task.text || /^\s*$/.test(task.text)) {
return
}
const newTasks = [task, ...tasks];
setTasks(newTasks);
};
const updateTask = (taskId, newValue) => {
if(!newValue.text || /^\s*$/.test(newValue.text)) {
return
}
setTasks(prev => prev.map(item => (item.id === taskId ? newValue : item)));
};
const removeTask = id => {
const removeArr = [...tasks].filter(task => task.id !== id);
setTasks(removeArr)
};
const completedTask = id => {
let updatedTasks = tasks.map(task => {
if (task.id === id) {
task.isComplete = !task.isComplete
}
return task
})
setTasks(updatedTasks);
};
return (
<div>
<h1>CabeƧalho</h1>
<TaskForm onSubmit={addTask}/>
<Task
tasks={tasks}
completedTask={completedTask}
removeTask={removeTask}
updateTask={updateTask} />
</div>
)
}
export default TaskList
=== TASK.JS ===
import React, { useState } from 'react'
import TaskForm from './TaskForm'
function Task(tasks, completedTask, removeTask, updateTask) {
const [edit, setEdit] = useState({
id: null,
value: ''
})
const submitUpdate = value => {
updateTask(edit.id, value)
setEdit({
id: null,
value: ''
})
}
if (edit.id) {
return <TaskForm edit={edit} onSubmit={submitUpdate} />;
}
return tasks.tasks.map((task, index) => (
<div className={task.isComplete ? 'task-row complete' : 'task-row'} key={index}>
{task.text}
<div className="buttons">
<button onClick={() => completedTask(task.id)} className='completed-icon'>done</button>
<button onClick={() => removeTask(task.id)} className='delete-icon'>delete</button>
<button onClick={() => setEdit({id: task.id, value: task.text})} className='edit-icon'>edit</button>
</div>
</div>
))
};
export default Task
=== TASKFORM.JS ===
import React, { useState, useEffect, useRef } from 'react'
function TaskForm(props) {
const [input, setInput] = useState(props.edit ? props.edit.value : '');
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus()
})
const handleChange = e => {
setInput(e.target.value);
}
const handleSubmit = e => {
e.preventDefault();
props.onSubmit({
id: Math.floor(Math.random() * 1000),
text: input
});
setInput('');
};
return (
<form className="task-form" onSubmit={handleSubmit}>
{props.edit ? (
<>
<input type="text" placeholder="Update your task" value={input} name="text" className="task-input" onChange={handleChange} ref={inputRef}/>
<button className="task-button edit" onChange={handleChange}>Update a task</button>
</>
) : (
<>
<input type="text" placeholder="Add a new task" value={input} name="text" className="task-input" onChange={handleChange} ref={inputRef}/>
<button className="task-button" onChange={handleChange}>Add a task</button>
</>
)}
</form>
)
}
export default TaskForm
Try this:
function Task({ tasks, completedTask, removeTask, updateTask }) {
// ...
}
You can also do this (semantically equivalent):
function Task(props) {
const { tasks, completedTask, removeTask, updateTask } = props;
// ...
}
As mentioned here:
The first parameter will be props object itself. You need to destructure the object.
You can read more about object destructuring here.

I can't call a function that I've assigned to a element in React

import React from "react";
import shortid from "shortid";
export default class App extends React.Component {
state = {
items: [],
text: ""
};
handleTextChange = event => {
this.setState({
text: event.target.value
});
};
addItem = event => {
event.preventDefault();
const newItem = {
text: this.state.text,
key: shortid.generate()
};
const newItems = [newItem, ...this.state.items];
this.setState({
items: newItems,
text: ""
});
};
deleteItem = event => {
event.preventDefault();
console.log("Can't reach this");
};
render() {
const items = this.state.items.map(function(item) {
return (
<li onClick={this.deleteItem} key={item.key}>
{item.text}
</li>
);
});
return (
<div className="appMain">
<div className="formWrapper">
<form onSubmit={this.addItem}>
<input
placeholder="Enter task: "
onChange={this.handleTextChange}
value={this.state.text}
/>
<button type="submit">Add</button>
</form>
</div>
<div className="listWrapper">
<ul>{items}</ul>
</div>
</div>
);
}
}
I'm trying to call the 'deleteItem' function when the elements are clicked. I'm checking this with a console.log inside the function. However it's not reaching that when I click on the element? I know it's probably a simple fix, but I've been stuck on it for a while now. can anyone help?
Update your items render code to following:
const items = this.state.items.map(item => {
return (
<li onClick={this.deleteItem} key={item.key}>
{item.text}
</li>
);
});
Notice the change from function to ES6 arrow syntax usage. For your case: this inside function won't be referring to class but map function.
Full code:
https://codesandbox.io/s/friendly-hopper-z09d1
The issue is you're using a normal function inside .map which don't have access to component this. So you're onClick is not invoking delete function if you replace with an arrow function it works.
something like this:
const items = this.state.items.map(item => {
return (
<li onClick={this.deleteItem} key={item.key}>
{item.text}
</li>
);
});
and working sample

i cant transfer data from react child to parent ang during click on child set value of input in parent

it is my first React App
i want create simple typeahead(autocomplete)
i want when i click on searched list of item, this item will show in value of my Parent input
now my click doesnt work, working only search by name
it is my parent
`
import React, { Component } from 'react';
import logo from './logo.svg';
import './Search.css';
import Sugg from './Sugg';
class Search extends Component {
constructor(props) {
super(props);
this.onSearch = this.onSearch.bind(this);
this.handleClickedItem = this.handleClickedItem.bind(this);
this.onClick = this.onClick.bind(this);
this.state = {
companies: [],
searchedList: [],
value: ''
}
}
componentDidMount() {
this.fetchApi();
console.log(this.state.companies);
}
fetchApi = ()=> {
const url = 'https://autocomplete.clearbit.com/v1/companies/suggest?query={companyName}';
fetch(url)
.then( (response) => {
let myData = response.json()
return myData;
})
.then((value) => {
let companies = value.map((company, i) => {
this.setState({
companies: [...this.state.companies, company]
})
})
console.log(this.state.companies);
});
}
onSearch(arr){
// this.setState({companies: arr});
};
handleInputChange = () => {
console.log(this.search.value);
let searched = [];
this.state.companies.map((company, i) => {
console.log(company.name);
console.log(company.domain);
const tempName = company.name.toLowerCase();
const tempDomain = company.domain.toLowerCase();
if(tempName.includes(this.search.value.toLowerCase()) || tempDomain.includes(this.search.value.toLowerCase())) {
searched.push(company);
}
})
console.log(searched);
this.setState({
searchedList: searched
})
if(this.search.value == '') {
this.setState({
searchedList: []
})
}
}
handleClickedItem(data) {
console.log(data);
}
onClick = e => {
console.log(e.target.value)
this.setState({ value: e.target.value});
};
render() {
return (
<div className="Search">
<header className="Search-header">
<img src={logo} className="Search-logo" alt="logo" />
<h1 className="Search-title">Welcome to React</h1>
</header>
<form>
<input
placeholder="Search for..."
ref={input => this.search = input}
onChange={this.handleInputChange}
/>
<Sugg searchedList={this.state.searchedList} onClick={this.onClick.bind(this)} />
</form>
</div>
);
}
}
export default Search;
`
and here my child component
i dont know how call correctly click event
import React from 'react';
const Sugg = (props) => {
console.log(props);
const options = props.searchedList.map((company, i) => (
<div key={i} >
<p onClick={() => this.props.onClick(this.props)}>{company.name}</p>
</div>
))
console.log(options);
return <div >{options}</div>
}
export default Sugg;
please help me who knows how it works
thanks a lot
In the parent you could modify your code:
onClick = company => {
console.log('company', company);
this.setState({ value: company.name});
};
and you don't need to bind this because onClick is an arrow function
<Sugg searchedList={this.state.searchedList} onClick={this.onClick} />
and in the child component, you need to use props from the parameters, not from the this context:
<p onClick={() =>props.onClick(company)}>{company.name}</p>

Data from redux-store visible in chrome dev tools but cannot actually access

I'm using react with redux, implementing redux-thunk for an action that does the following:
Thunk calls axios.get()
First then() statement maps over JSON to convert keys and values to front end specifications
Second then() statement iterates over one property on JSON (0 - 5 items) creating an array of axios.get()calles, wrapped in promises, for each. This array is passed to an async/wait function ensuring these promises are all resolved before moving on.
Third then() statement iterates over list again, removing any undefined values, and then fires off action creator successfully. Logging the data here on Chrome dev tools verifies that all data is present, and there are no unresolved promises.
Logging this payload in Chrome dev tools from the reducers and into the container, which maps over data, results in the same issue. Data is present in console, but not accessible anywhere.
I'll provide the working js below, although it does not work in this snippet:
/* - - - - - - Actions - - - - - - -
Using thunk, promises, and async/await to insure data is present before sending to reducer
*/
export function getSingleMessage(id, callback) {
return function (dispatch) {
const request = axios.get(`${ROOT_URL}getDialogue.php?message_id=${id}`)
.then((resp) => {
// Just reformatting JSON
const { data } = resp;
console.log('message: ', data)
let formValues = {};
let messageIds = [];
formValues.dialogue_query = data.message_query;
formValues.dialogue_text = data.message;
formValues.message_id = data.messages_id;
if (data.attachment) {
formValues.attachment_text = data.attachment.text;
formValues.attachment_fallback = data.attachment.fallback;
formValues.image_url = data.attachment.image_url;
if (data.attachment.actions) {
/*
1) these buttons need unique id's
2) the last unique id needs to not collide with any other id's of new buttons
// Map through actions
if key is 'name'
button_text
if key is 'type'
action
*/
let key = 0;
formValues.buttons = data.attachment.actions.map( action => {
let newAction = {};
Object.keys(action).forEach( key => {
let curr = action[key];
let newCurrArr;
let newCurr;
if (key === 'name') {
newAction.button_text = curr;
}
if (key === 'value') {
if (curr.includes('Value[')) {
newCurrArr = curr.split('=');
newCurr = newCurrArr.pop();
newAction.button_action = newCurr;
curr = newCurrArr.join('=')
console.log('CURRRRRRR: ', curr)
}
if (curr.includes('message_id=')) {
newCurrArr = curr.split('=').pop();
console.log('NEWCURRARR: ', newCurrArr)
let newNewCurrArr = newCurrArr.split('&');
console.log('NEWNEWCURRARR: ', newNewCurrArr)
newCurr = newNewCurrArr.shift();
messageIds.push(newCurr);
newAction.message_id = Number(newCurr);
}
}
});
newAction.id = key;
key++;
return newAction;
})
}
}
return [formValues, messageIds]
})
.then((resp) => {
// creating array of promises, and resolving with async/await
const formValues = resp[0];
const messageIds = resp[1];
const promises = messageIds.map((id) => axios.get(`${ROOT_URL}getDialogue.php?message_id=${id}`));
console.log('PROMISES!!!!!! ', promises)
if (formValues.buttons) {
async function getPreviewMessages() {
let resolvedPreviewMessages = await Promise.all(promises);
return formValues.buttons.map((button, i) => {
// setting previewMessages to buttons
button.previewMessage = resolvedPreviewMessages[i];
return button;
})
}
getPreviewMessages();
}
return formValues;
})
.then((formValues) => {
//
console.log('RESP: ', formValues)
// cleans up any null or undefined values
for (let key in formValues) {
if (formValues[key] === 'null' || formValues[key] === 'undefined') {
formValues[key] = '';
console.log(`formValues[${key}] = ${formValues[key]}`);
}
console.log(`formValues[${key}] = ${formValues[key]}`);
}
console.log('formValues: ', formValues)
dispatch(fireOffSingleMessage({data: formValues}));
})
.catch((err) => {
console.log(err)
return err;
})
console.log('requesssssssst: ', request)
callback();
}
}
function fireOffSingleMessage(request) {
// data is all resolved in Chrome Dev Tools
console.log('FIRED OFF SINGLE MESSAGE~~~~~~', request)
return {
type: GET_SINGLE_MESSAGE,
payload: request,
}
}
// - - - - - - Reducer - - - - - - -
import { GET_SINGLE_MESSAGE, REFRESH_SINGLE_MESSAGE, ADD_BUTTON_EDIT, EDIT_BUTTON_EDIT, DELETE_BUTTON_EDIT } from '../constants';
export default (state = {}, action) => {
switch(action.type) {
case GET_SINGLE_MESSAGE:
console.log('Data is present!: ', action.payload.data.buttons)
return action.payload;
case REFRESH_SINGLE_MESSAGE:
return {}
// Not involved
case ADD_BUTTON_EDIT:
console.log('ADD_BUTTON_EDIT reducer fired off!', state);
if (Array.isArray(state.data.buttons) && state.data.buttons.length > 0) {
return {
...state,
data: {... state.data, buttons: [...state.data.buttons, action.button]}
};
} else {
return {
...state,
data: {... state.data, buttons: [action.button]}
};
}
case EDIT_BUTTON_EDIT:
console.log('EDIT_BUTTON_EDIT reducer fired off!', state);
const newStateEdit = state.data.buttons.map(button => {
if (button.id === action.button.id) {
console.log('button.id: ', button.id)
console.log('action.button.id: ', action.button.id)
return action.button;
} else {
return button;
}
})
return {
...state,
data: {... state.data, buttons: newStateEdit}
};
case DELETE_BUTTON_EDIT:
console.log('DELETE_BUTTON_EDIT reducer fired off!', state);
const newStateDelete = state.data.buttons.filter(button => button.id !== action.target);
return {
...state,
data: {... state.data, buttons: newStateDelete}
};
default:
return state;
}
}
// - - - - Root Reducer - - - - -
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import getAllDialogs from './getAllDialogs-reducer.js';
import handleMessages from './messages-reducer.js';
import handleMessage from './message-reducer.js'
import handleMessageId from './messageId-reducer.js'
import updateButtons from './buttons-reducer.js';
import renderButtonForm from './buttonForm-reducer.js';
import handleId from './buttonId-reducer.js';
import handleNewMessageQuery from './newMessageQuery-reducer.js';
import handleMessageQueries from './messageQueries-reducer.js';
import handleButton from './button-reducer.js';
import handleReRenderToggle from './reRenderToggle-reducer.js';
import handleContext from './context-reducer.js';
const rootReducer = combineReducers({
dialogs: getAllDialogs,
messages: handleMessages,
message: handleMessage,
messageId: handleMessageId,
buttons: updateButtons,
buttonForm: renderButtonForm,
button: handleButton,
buttonId: handleId,
reRender: handleReRenderToggle,
newMessageQuery: handleNewMessageQuery,
messageQueries: handleMessageQueries,
context: handleContext,
form: formReducer,
});
export default rootReducer;
// - - - - - container - - - - -
renderFakeButtons(buttons = []) {
const _that = this;
buttons.forEach((button, i) => {console.log(button)});
const { history, deleteButton, renderButtonForm, reRenderToggle, getSingleMessage, getSinglePreviewMessage } = this.props;
// DATA LOGS IN DEV TOOLS!!!!
console.log('BUTTONS: ', buttons)
return buttons.map((button) => {
// DATA LOGS IN DEV TOOLS!!!
console.log('BUTTON: ', button)
return (
<div
className="top-margin"
key={button.id}>
<div className="row">
<Card>
<CardHeader
title={`Button Text: ${button.button_text}`}
subtitle={`Button Action: ${button.button_action}`}
actAsExpander={true}
showExpandableButton={true}
/>
<CardText expandable={true}>
<div>
{/*THIS IS WHERE I WOULD ACCESS THE PROPERTY EXPLICITLY*/}
{JSON.stringify(button)}
</div>
</CardText>
<CardActions>
<FlatButton
label={`Next Message: ${button.message_id}`}
onTouchTap={function(){
getSingleMessage(button.message_id, () => {
history.push(`/message/edit/${button.message_id}`);
})
}
}
/>
<FlatButton
label="Delete Button"
onTouchTap={() => { deleteButton(button.id, 'edit'); reRenderToggle(); }}
/>
</CardActions>
</Card>
</div>
</div>
)}
);
}
render() {
const _that = this;
const { state, onSubmit, onTest, onBack, renderMessageQuerySelect, renderInputFields, renderTextAreaFields, injectButtonForm, renderUserTestSelectOptions } = this;
const { messages, getMessageId, messageId, handleSubmit, renderButtonForm, buttons, reset, buttonForm, refreshSingleMessage, toggleMessageQueryField, newMessageQuery, initialValues, addButtons } = this.props;
let titleName;
if (messages.messages && messages.messages.dialog_name) {
titleName = messages.messages.dialog_name;
} else if (messages.messages && messages.messages.messageQuery) {
titleName = messages.messages.messageQuery
}
return (
<div>
<MuiThemeProvider>
<div className="row">
<div className="col-md-6">
<form onSubmit={handleSubmit(onSubmit.bind(this))}>
<div className="form-group">
<h4> Edit Message {messageId} from "{titleName}" </h4>
<Field
label="Message Query"
name="dialogue_query"
component={newMessageQuery ? renderInputFields : renderMessageQuerySelect.bind(this)}
/>
New
<input
type="checkbox"
onClick={toggleMessageQueryField}
/>
</div>
<Field
label="Text of Message"
name="dialogue_text"
component={renderTextAreaFields}
/>
<Field
label="Attachment Text"
name="attachment_text"
component={renderInputFields}
/>
{ state.error !== null ? <div className="form-group has-danger"><div className="text-help">{state.error}</div></div> : null }
<Field
label="Attachment Fallback"
name="attachment_fallback"
component={renderInputFields}
/>
{ state.error !== null ? <div className="form-group has-danger"><div className="text-help">{state.error}</div></div> : null }
<Field
label="Image Url"
name="image_url"
component={renderInputFields}
/>
<div className="form-group">
<div>
<div
className="btn btn-primary"
onClick={function() {
renderButtonForm();
}}
>
Click to Add Buttons
</div>
<div>
{ initialValues && initialValues.buttons ? _that.renderFakeButtons(initialValues.buttons) : '' }
</div>
</div>
</div>
<Field
label="Test Primary User"
name="primary_test_user_id"
component={renderUserTestSelectOptions}
/>
<Field
label="Test Secondary User"
name="secondary_test_user_id"
component={renderUserTestSelectOptions}
/>
<button
type="submit"
className="btn btn-primary"
>
Submit
</button>
<button
type="submit"
className="btn btn-primary buttons-margin"
onClick={handleSubmit(onTest.bind(this))}
>
Test
</button>
<div className="btn btn-primary buttons-margin" onClick={reset}>Reset to Original</div>
<div className="btn btn-danger buttons-margin" onClick={onBack.bind(this)}>Back</div>
</form>
</div>
<div className="col-md-5">
<div className="row">
<div className="col-md-12">
{injectButtonForm(buttonForm)}
</div>
</div>
<div className="row">
<div className="col-md-12">
<div className="bottom-top-margin">
</div>
</div>
</div>
</div>
</div>
</MuiThemeProvider>
</div>
);
}
}
function validate(values) {
const errors = {};
if (!values.dialogue_text) {
errors.dialogue_text = "Enter message text";
}
return errors;
}
MessageEdit = reduxForm({
validate,
form: 'MessageEditForm', // a unique name for this form
enableReinitialize: true,
})(MessageEdit)
MessageEdit = connect(
state => {
const newState = {
dialogs: state.dialogs,
messages: state.messages,
messageId: state.messageId,
initialValues: state.message.data,
buttonForm: state.buttonForm.isRendered,
buttonId: state.buttonId,
messageQueries: state.messageQueries,
reRender: state.reRender,
newMessageQuery: state.newMessageQuery,
context: state.context,
}
console.log('MessageEdit newState: ', newState);
return newState },
{ addButtons, postNewMessage, testMessage, getSingleMessage, refreshSingleMessage, deleteMessage, getDialogMessages, refreshButtons, deleteButton, refreshButtonId, refreshButtons, renderButtonForm, unRenderButtonForm, toggleMessageQueryField, getMessageId, getMessageQueries, reRenderToggle }
)(MessageEdit)
export default MessageEdit;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Why is my data available in my THUNK, according to Chrome dev tools, but not actually accessible?
In your render function you have...
{ initialValues && initialValues.buttons ? _that.renderFakeButtons(initialValues.buttons) : '' }
which I'm assuming is not showing up. You don't really need to pass initialValues as an argument here, instead just pass all of the props.
Try instead doing something like this:
renderFakeButtons = (props) => {
if (!props.initialValues.buttons) return;
// do some rendering here
}
render() {
const MaybeFakeButtons = this.renderFakeButtons;
return (
<div><MaybeFakeButtons {...this.props} /></div>
)
}
My guess is that React isn't recognizing the nested updates so the initialValues ternary part of the tree is getting updated. Try spreading all of the props (shown above) and see if it works.

Categories

Resources