I've been playing with this pen for awhile as an exercise with various React hooks. I'm wanting to dynamically render a list of names from a context, and for that list to re-render after posting a new person object to the context array.
It seems I've gotten close, as submitting the form logs the updated peopleNameList
however the context doesn't appear to be updating properly, and the form submission is crashing the pen. Am I missing something small, or am I missing a larger composition issue?
const {createContext, useState, useEffect, useContext, useReducer} = React;
// https://stackoverflow.com/questions/55757761/handle-an-input-with-react-hooks
// https://upmostly.com/tutorials/how-to-use-the-usecontext-hook-in-react
var dataStore = {
people: [
{
firstName: 'Jim',
lastName: 'Smith',
email: 'jims@email.com',
age: 32,
job: 'Engineer',
transportation: 'car'
},
{
firstName: 'Sarah',
lastName: 'Alton',
email: 'salton@email.com',
age: 24,
job: 'Programmer',
transportation: 'bike'
},
{
firstName: 'Leslie',
lastName: 'Leeland',
email: 'lleeland@woohoo.com',
age: 24,
job: 'Teacher',
transportation: 'Scooter'
}
]
};
// define that context takes an object and an update function
const PeopleContext = createContext();
const PeopleContextProvider = (props) => {
// dataStore.people as default context state, with a reducer dispatch returned for use with useContext hook to add a new person from the form
// note you cannot push to a state array because this mutates state directly
// https://medium.com/javascript-in-plain-english/how-to-add-to-an-array-in-react-state-3d08ddb2e1dc
const [peopleStore, addNewPerson] = useReducer((peopleStore, newPerson) =>
peopleStore.concat(newPerson), dataStore.people);
return(
// Context Provider takes a value prop
<PeopleContext.Provider value={[peopleStore, addNewPerson]}>
{props.children}
</PeopleContext.Provider>
)
}
function RecentPeople(props) {
// peopleStore equivalent to value prop of PeopleContext provider
const [peopleStore, addNewPerson] = useContext(PeopleContext);
const addToPeopleNameList = () => {
let peopleNameList = [];
_.map(peopleStore, (i) => {
let name = `${i.firstName} ${i.lastName}`;
peopleNameList.push(name);
return peopleNameList;
});
console.log(`peopleNameList: ${peopleNameList}`);
return(<>
{_.map(peopleNameList, (name, i) => <li key={i}>{name}</li>)}
</>)
};
useEffect(() => addToPeopleNameList(), [peopleStore]);
return(
<>
<h3>Recent People</h3>
<ul>
{addToPeopleNameList()}
</ul>
</>
);
} // end RecentPeople
function Input(props) {
let {inputId, value, handleChange } = props;
let inputLabel = _.startCase(props.inputId);
return(
<div class="input-group">
<label for={inputId}>{inputLabel}</label>
<input id={inputId} value={value} name={inputId} onChange={handleChange} />
</div>
);
};
const inputFields = [ 'firstName', 'lastName', 'email', 'age', 'job', 'transportation' ]
const initialFormState = {
firstName: '',
lastName: '',
email: '',
age: null,
job: '',
transportation: ''
};
function NewUserForm(props) {
// pass in the reducer function inline to concat existing and new state
const [inputValues, setInputValues] = useReducer((state, newState) => ({...state, ...newState}), initialFormState);
const [peopleStore, addNewPerson] = useContext(PeopleContext);
function handleOnChange(event) {
const {name, value} = event.target;
// call the reducer update function, passing in existing formstate with state of current input
setInputValues({...inputValues, [name]: value });
}
function handleSubmit(event) {
event.preventDefault();
addNewPerson(inputValues);
console.log(peopleStore);
setInputValues({}, {...initialFormState});
}
// don't pass event here
function handleReset(props) {
setInputValues({}, {...initialFormState});
}
return(
<form id="new-user-form" onSubmit={handleSubmit}>
{_.map(inputFields, (fieldName) => (<Input key={fieldName} type="text" name={fieldName} inputId={fieldName} handleChange={handleOnChange} />))}
<button type="submit" value="submit">Add User</button>
<button type="reset" style={{"marginLeft": "20px"}} onClick={handleReset}>Reset Form</button>
</form>
);
} // form
function App() {
return(
<PeopleContextProvider>
<div id="main">
<h1>Hello!</h1>
<NewUserForm />
<RecentPeople />
</div>
</PeopleContextProvider>
);
};
ReactDOM.render(<App />, document.getElementById('App'));