简体   繁体   English

尝试从表单提交和触发列表渲染更新 React 上下文

[英]Trying to Update React Context from Form Submit and Trigger List Rerender

I've been playing with this pen for awhile as an exercise with various React hooks.我用这支笔玩了一段时间,作为各种 React 钩子的练习。 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.似乎我已经接近了,因为提交表单记录了更新的peopleNameList但是上下文似乎没有正确更新,并且表单提交使笔崩溃。 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'));

It appears the major culprit was useEffect can't return the render list function directly, and wrapping it in brackets was required to scope the function.看来主要的罪魁祸首是 useEffect 无法直接返回渲染列表函数,并且需要将其包装在括号中以限定该函数的范围。 With a couple other name changes and small tweaks, the corrected pen is here and now shows the context's dispatch function correctly being called and the list component listening for the change and rerendering the list.通过其他一些名称更改和小调整,更正的笔在这里,现在显示正确调用上下文的调度函数以及列表组件侦听更改并重新呈现列表。 Probably some optimization that could be done there to only show differing items.可能可以在那里进行一些优化以仅显示不同的项目。

https://codepen.io/smallreflection/pen/yLyLQge?editors=1011 https://codepen.io/smallreflection/pen/yLyLQge?editors=1011

useEffect(() => { renderPeopleNameList() }, [peopleStoreContext]);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM